File indexing completed on 2025-01-19 03:55:42
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-05-21 0007 * Description : widget to display a list of items 0008 * 0009 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2008-2010 by Andi Clemens <andi dot clemens at googlemail dot com> 0011 * SPDX-FileCopyrightText: 2009-2010 by Luka Renko <lure at kubuntu dot org> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "ditemslist_p.h" 0018 0019 namespace Digikam 0020 { 0021 0022 class Q_DECL_HIDDEN DItemsList::Private 0023 { 0024 public: 0025 0026 explicit Private() 0027 : allowRAW (true), 0028 allowDuplicate (false), 0029 controlButtonsEnabled(true), 0030 iconSize (48), 0031 addButton (nullptr), 0032 removeButton (nullptr), 0033 moveUpButton (nullptr), 0034 moveDownButton (nullptr), 0035 clearButton (nullptr), 0036 loadButton (nullptr), 0037 saveButton (nullptr), 0038 extraWidget (nullptr), 0039 progressPix (nullptr), 0040 progressCount (0), 0041 progressTimer (nullptr), 0042 listView (nullptr), 0043 iface (nullptr), 0044 isLessThan (nullptr) 0045 { 0046 thumbLoadThread = ThumbnailLoadThread::defaultThread(); 0047 } 0048 0049 bool allowRAW; 0050 bool allowDuplicate; 0051 bool controlButtonsEnabled; 0052 int iconSize; 0053 0054 CtrlButton* addButton; 0055 CtrlButton* removeButton; 0056 CtrlButton* moveUpButton; 0057 CtrlButton* moveDownButton; 0058 CtrlButton* clearButton; 0059 CtrlButton* loadButton; 0060 CtrlButton* saveButton; 0061 QWidget* extraWidget; ///< Extra widget append to the end of control buttons layout. 0062 0063 QList<QUrl> processItems; 0064 DWorkingPixmap* progressPix; 0065 int progressCount; 0066 QTimer* progressTimer; 0067 0068 DItemsListView* listView; 0069 ThumbnailLoadThread* thumbLoadThread; 0070 0071 DInfoInterface* iface; 0072 DItemsListIsLessThanHandler isLessThan; 0073 }; 0074 0075 DItemsList::DItemsList(QWidget* const parent) 0076 : QWidget(parent), 0077 d (new Private) 0078 { 0079 d->progressPix = new DWorkingPixmap(this); 0080 d->listView = new DItemsListView(this); 0081 d->listView->setSelectionMode(QAbstractItemView::ExtendedSelection); 0082 0083 // -------------------------------------------------------- 0084 0085 d->addButton = new CtrlButton(QIcon::fromTheme(QLatin1String("list-add")).pixmap(16, 16), this); 0086 d->removeButton = new CtrlButton(QIcon::fromTheme(QLatin1String("list-remove")).pixmap(16, 16), this); 0087 d->moveUpButton = new CtrlButton(QIcon::fromTheme(QLatin1String("go-up")).pixmap(16, 16), this); 0088 d->moveDownButton = new CtrlButton(QIcon::fromTheme(QLatin1String("go-down")).pixmap(16, 16), this); 0089 d->clearButton = new CtrlButton(QIcon::fromTheme(QLatin1String("edit-clear")).pixmap(16, 16), this); 0090 d->loadButton = new CtrlButton(QIcon::fromTheme(QLatin1String("document-open")).pixmap(16, 16), this); 0091 d->saveButton = new CtrlButton(QIcon::fromTheme(QLatin1String("document-save")).pixmap(16, 16), this); 0092 0093 d->addButton->setToolTip(i18nc("@info", "Add new images to the list")); 0094 d->removeButton->setToolTip(i18nc("@info", "Remove selected images from the list")); 0095 d->moveUpButton->setToolTip(i18nc("@info", "Move current selected image up in the list")); 0096 d->moveDownButton->setToolTip(i18nc("@info", "Move current selected image down in the list")); 0097 d->clearButton->setToolTip(i18nc("@info", "Clear the list.")); 0098 d->loadButton->setToolTip(i18nc("@info", "Load a saved list.")); 0099 d->saveButton->setToolTip(i18nc("@info", "Save the list.")); 0100 0101 d->progressTimer = new QTimer(this); 0102 0103 // -------------------------------------------------------- 0104 0105 setIconSize(d->iconSize); 0106 setControlButtons(Add | Remove | MoveUp | MoveDown | Clear | Save | Load ); // add all buttons (default) 0107 setControlButtonsPlacement(ControlButtonsBelow); // buttons on the bottom (default) 0108 enableDragAndDrop(true); // enable drag and drop (default) 0109 0110 // -------------------------------------------------------- 0111 0112 connect(d->listView, &DItemsListView::signalAddedDropedItems, 0113 this, &DItemsList::slotAddImages); 0114 0115 connect(d->thumbLoadThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)), 0116 this, SLOT(slotThumbnail(LoadingDescription,QPixmap))); 0117 0118 connect(d->listView, &DItemsListView::signalItemClicked, 0119 this, &DItemsList::signalItemClicked); 0120 0121 connect(d->listView, &DItemsListView::signalContextMenuRequested, 0122 this, &DItemsList::signalContextMenuRequested); 0123 0124 // queue this connection because itemSelectionChanged is emitted 0125 // while items are deleted, and accessing selectedItems at that 0126 // time causes a crash ... 0127 0128 connect(d->listView, &DItemsListView::itemSelectionChanged, 0129 this, &DItemsList::slotImageListChanged, Qt::QueuedConnection); 0130 0131 connect(this, &DItemsList::signalImageListChanged, 0132 this, &DItemsList::slotImageListChanged); 0133 0134 // -------------------------------------------------------- 0135 0136 connect(d->addButton, &CtrlButton::clicked, 0137 this, &DItemsList::slotAddItems); 0138 0139 connect(d->removeButton, &CtrlButton::clicked, 0140 this, &DItemsList::slotRemoveItems); 0141 0142 connect(d->moveUpButton, &CtrlButton::clicked, 0143 this, &DItemsList::slotMoveUpItems); 0144 0145 connect(d->moveDownButton, &CtrlButton::clicked, 0146 this, &DItemsList::slotMoveDownItems); 0147 0148 connect(d->clearButton, &CtrlButton::clicked, 0149 this, &DItemsList::slotClearItems); 0150 0151 connect(d->loadButton, &CtrlButton::clicked, 0152 this, &DItemsList::slotLoadItems); 0153 0154 connect(d->saveButton, &CtrlButton::clicked, 0155 this, &DItemsList::slotSaveItems); 0156 0157 connect(d->progressTimer, &QTimer::timeout, 0158 this, &DItemsList::slotProgressTimerDone); 0159 0160 // -------------------------------------------------------- 0161 0162 QTimer::singleShot(1000, this, SIGNAL(signalImageListChanged())); 0163 } 0164 0165 DItemsList::~DItemsList() 0166 { 0167 delete d; 0168 } 0169 0170 void DItemsList::enableControlButtons(bool enable) 0171 { 0172 d->controlButtonsEnabled = enable; 0173 slotImageListChanged(); 0174 } 0175 0176 void DItemsList::enableDragAndDrop(const bool enable) 0177 { 0178 d->listView->enableDragAndDrop(enable); 0179 } 0180 0181 void DItemsList::appendControlButtonsWidget(QWidget* const widget) 0182 { 0183 d->extraWidget = widget; 0184 } 0185 0186 QBoxLayout* DItemsList::setControlButtonsPlacement(ControlButtonPlacement placement) 0187 { 0188 delete layout(); 0189 0190 QBoxLayout* lay = nullptr; // Layout instance to return; 0191 const int spacing = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0192 QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0193 0194 QGridLayout* const mainLayout = new QGridLayout; 0195 mainLayout->addWidget(d->listView, 1, 1, 1, 1); 0196 mainLayout->setRowStretch(1, 10); 0197 mainLayout->setColumnStretch(1, 10); 0198 mainLayout->setContentsMargins(spacing, spacing, spacing, spacing); 0199 mainLayout->setSpacing(spacing); 0200 0201 // -------------------------------------------------------- 0202 0203 QHBoxLayout* const hBtnLayout = new QHBoxLayout; 0204 hBtnLayout->addWidget(d->moveUpButton); 0205 hBtnLayout->addWidget(d->moveDownButton); 0206 hBtnLayout->addWidget(d->addButton); 0207 hBtnLayout->addWidget(d->removeButton); 0208 hBtnLayout->addWidget(d->loadButton); 0209 hBtnLayout->addWidget(d->saveButton); 0210 hBtnLayout->addWidget(d->clearButton); 0211 hBtnLayout->addStretch(1); 0212 0213 if (d->extraWidget) 0214 { 0215 hBtnLayout->addWidget(d->extraWidget); 0216 } 0217 0218 // -------------------------------------------------------- 0219 0220 QVBoxLayout* const vBtnLayout = new QVBoxLayout; 0221 vBtnLayout->addWidget(d->moveUpButton); 0222 vBtnLayout->addWidget(d->moveDownButton); 0223 vBtnLayout->addWidget(d->addButton); 0224 vBtnLayout->addWidget(d->removeButton); 0225 vBtnLayout->addWidget(d->loadButton); 0226 vBtnLayout->addWidget(d->saveButton); 0227 vBtnLayout->addWidget(d->clearButton); 0228 vBtnLayout->addStretch(1); 0229 0230 if (d->extraWidget) 0231 { 0232 vBtnLayout->addWidget(d->extraWidget); 0233 } 0234 0235 // -------------------------------------------------------- 0236 0237 switch (placement) 0238 { 0239 case ControlButtonsAbove: 0240 { 0241 lay = hBtnLayout; 0242 mainLayout->addLayout(hBtnLayout, 0, 1, 1, 1); 0243 delete vBtnLayout; 0244 break; 0245 } 0246 0247 case ControlButtonsBelow: 0248 { 0249 lay = hBtnLayout; 0250 mainLayout->addLayout(hBtnLayout, 2, 1, 1, 1); 0251 delete vBtnLayout; 0252 break; 0253 } 0254 0255 case ControlButtonsLeft: 0256 { 0257 lay = vBtnLayout; 0258 mainLayout->addLayout(vBtnLayout, 1, 0, 1, 1); 0259 delete hBtnLayout; 0260 break; 0261 } 0262 0263 case ControlButtonsRight: 0264 { 0265 lay = vBtnLayout; 0266 mainLayout->addLayout(vBtnLayout, 1, 2, 1, 1); 0267 delete hBtnLayout; 0268 break; 0269 } 0270 0271 case NoControlButtons: 0272 default: 0273 { 0274 delete vBtnLayout; 0275 delete hBtnLayout; 0276 0277 // set all buttons invisible 0278 0279 setControlButtons(ControlButtons()); 0280 0281 if (d->extraWidget) 0282 { 0283 d->extraWidget->setVisible(false); 0284 } 0285 0286 break; 0287 } 0288 } 0289 0290 setLayout(mainLayout); 0291 0292 return lay; 0293 } 0294 0295 void DItemsList::setControlButtons(ControlButtons buttonMask) 0296 { 0297 d->addButton->setVisible(buttonMask & Add); 0298 d->removeButton->setVisible(buttonMask & Remove); 0299 d->moveUpButton->setVisible(buttonMask & MoveUp); 0300 d->moveDownButton->setVisible(buttonMask & MoveDown); 0301 d->clearButton->setVisible(buttonMask & Clear); 0302 d->loadButton->setVisible(buttonMask & Load); 0303 d->saveButton->setVisible(buttonMask & Save); 0304 } 0305 0306 void DItemsList::setIface(DInfoInterface* const iface) 0307 { 0308 d->iface = iface; 0309 } 0310 0311 DInfoInterface* DItemsList::iface() const 0312 { 0313 return d->iface; 0314 } 0315 0316 void DItemsList::setAllowDuplicate(bool allow) 0317 { 0318 d->allowDuplicate = allow; 0319 } 0320 0321 void DItemsList::setAllowRAW(bool allow) 0322 { 0323 d->allowRAW = allow; 0324 } 0325 0326 void DItemsList::setIconSize(int size) 0327 { 0328 if (size < 16) 0329 { 0330 d->iconSize = 16; 0331 } 0332 else if (size > 128) 0333 { 0334 d->iconSize = 128; 0335 } 0336 else 0337 { 0338 d->iconSize = size; 0339 } 0340 0341 d->listView->setIconSize(QSize(iconSize(), iconSize())); 0342 } 0343 0344 int DItemsList::iconSize() const 0345 { 0346 return d->iconSize; 0347 } 0348 0349 void DItemsList::loadImagesFromCurrentSelection() 0350 { 0351 bool selection = checkSelection(); 0352 0353 if (selection && d->iface) 0354 { 0355 QList<QUrl> images = d->iface->currentSelectedItems(); 0356 0357 if (!images.isEmpty()) 0358 { 0359 slotAddImages(images); 0360 } 0361 } 0362 else 0363 { 0364 loadImagesFromCurrentAlbum(); 0365 } 0366 } 0367 0368 void DItemsList::loadImagesFromCurrentAlbum() 0369 { 0370 if (!d->iface) 0371 { 0372 return; 0373 } 0374 0375 QList<QUrl> images = d->iface->currentAlbumItems(); 0376 0377 if (!images.isEmpty()) 0378 { 0379 slotAddImages(images); 0380 } 0381 } 0382 0383 bool DItemsList::checkSelection() 0384 { 0385 if (!d->iface) 0386 { 0387 return false; 0388 } 0389 0390 QList<QUrl> images = d->iface->currentSelectedItems(); 0391 0392 return (!images.isEmpty()); 0393 } 0394 0395 void DItemsList::slotAddImages(const QList<QUrl>& list) 0396 { 0397 if (list.count() == 0) 0398 { 0399 return; 0400 } 0401 0402 QList<QUrl> urls; 0403 bool raw = false; 0404 0405 for (QList<QUrl>::ConstIterator it = list.constBegin() ; it != list.constEnd() ; ++it) 0406 { 0407 QUrl imageUrl = *it; 0408 0409 // Check if the new item already exist in the list. 0410 0411 bool found = false; 0412 0413 QTreeWidgetItemIterator iter(d->listView); 0414 0415 while (*iter) 0416 { 0417 DItemsListViewItem* const item = dynamic_cast<DItemsListViewItem*>(*iter); 0418 0419 if (item && (item->url() == imageUrl)) 0420 { 0421 found = true; 0422 } 0423 0424 ++iter; 0425 } 0426 0427 if (d->allowDuplicate || !found) 0428 { 0429 // if RAW files are not allowed, skip the image 0430 0431 if (!d->allowRAW && DRawDecoder::isRawFile(imageUrl)) 0432 { 0433 raw = true; 0434 continue; 0435 } 0436 0437 new DItemsListViewItem(listView(), imageUrl); 0438 urls.append(imageUrl); 0439 } 0440 } 0441 0442 Q_EMIT signalAddItems(urls); 0443 Q_EMIT signalImageListChanged(); 0444 Q_EMIT signalFoundRAWImages(raw); 0445 } 0446 0447 void DItemsList::slotAddItems() 0448 { 0449 KSharedConfigPtr config = KSharedConfig::openConfig(); 0450 KConfigGroup grp = config->group(objectName()); 0451 QUrl lastFileUrl = QUrl::fromLocalFile(grp.readEntry("Last Image Path", 0452 QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))); 0453 0454 QList<QUrl> urls = ImageDialog::getImageURLs(this, lastFileUrl); 0455 0456 if (!urls.isEmpty()) 0457 { 0458 slotAddImages(urls); 0459 grp.writeEntry("Last Image Path", urls.first().adjusted(QUrl::RemoveFilename).toLocalFile()); 0460 config->sync(); 0461 } 0462 } 0463 0464 void DItemsList::slotRemoveItems() 0465 { 0466 QList<QTreeWidgetItem*> selectedItemsList = d->listView->selectedItems(); 0467 QList<int> itemsIndex; 0468 0469 for (QList<QTreeWidgetItem*>::const_iterator it = selectedItemsList.constBegin() ; 0470 it != selectedItemsList.constEnd() ; ++it) 0471 { 0472 DItemsListViewItem* const item = dynamic_cast<DItemsListViewItem*>(*it); 0473 0474 if (item) 0475 { 0476 itemsIndex.append(d->listView->indexFromItem(item).row()); 0477 0478 if (d->processItems.contains(item->url())) 0479 { 0480 d->processItems.removeAll(item->url()); 0481 } 0482 0483 d->listView->removeItemWidget(*it, 0); 0484 delete *it; 0485 } 0486 } 0487 0488 Q_EMIT signalRemovedItems(itemsIndex); 0489 Q_EMIT signalImageListChanged(); 0490 } 0491 0492 void DItemsList::slotMoveUpItems() 0493 { 0494 // move above item down, then we don't have to fix the focus 0495 0496 QModelIndex curIndex = listView()->currentIndex(); 0497 0498 if (!curIndex.isValid()) 0499 { 0500 return; 0501 } 0502 0503 QModelIndex aboveIndex = listView()->indexAbove(curIndex); 0504 0505 if (!aboveIndex.isValid()) 0506 { 0507 return; 0508 } 0509 0510 QTreeWidgetItem* const temp = listView()->takeTopLevelItem(aboveIndex.row()); 0511 listView()->insertTopLevelItem(curIndex.row(), temp); 0512 0513 // this is a quick fix. We loose the extra tags in flickr upload, but at list we don't get a crash 0514 0515 DItemsListViewItem* const uw = dynamic_cast<DItemsListViewItem*>(temp); 0516 0517 if (uw) 0518 { 0519 uw->updateItemWidgets(); 0520 } 0521 0522 Q_EMIT signalImageListChanged(); 0523 Q_EMIT signalMoveUpItem(); 0524 } 0525 0526 void DItemsList::slotMoveDownItems() 0527 { 0528 // move below item up, then we don't have to fix the focus 0529 0530 QModelIndex curIndex = listView()->currentIndex(); 0531 0532 if (!curIndex.isValid()) 0533 { 0534 return; 0535 } 0536 0537 QModelIndex belowIndex = listView()->indexBelow(curIndex); 0538 0539 if (!belowIndex.isValid()) 0540 { 0541 return; 0542 } 0543 0544 QTreeWidgetItem* const temp = listView()->takeTopLevelItem(belowIndex.row()); 0545 listView()->insertTopLevelItem(curIndex.row(), temp); 0546 0547 // This is a quick fix. We can loose extra tags in uploader, but at least we don't get a crash 0548 0549 DItemsListViewItem* const uw = dynamic_cast<DItemsListViewItem*>(temp); 0550 0551 if (uw) 0552 { 0553 uw->updateItemWidgets(); 0554 } 0555 0556 Q_EMIT signalImageListChanged(); 0557 Q_EMIT signalMoveDownItem(); 0558 } 0559 0560 void DItemsList::slotClearItems() 0561 { 0562 listView()->selectAll(); 0563 slotRemoveItems(); 0564 listView()->clear(); 0565 } 0566 0567 void DItemsList::slotLoadItems() 0568 { 0569 KSharedConfigPtr config = KSharedConfig::openConfig(); 0570 KConfigGroup grp = config->group(objectName()); 0571 QUrl lastFileUrl = QUrl::fromLocalFile(grp.readEntry("Last Images List Path", 0572 QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation))); 0573 QUrl loadLevelsFile; 0574 loadLevelsFile = DFileDialog::getOpenFileUrl(this, 0575 i18nc("@title:window", "Select the Image File List to Load"), lastFileUrl, 0576 i18nc("@option", "All Files (*)")); 0577 0578 if (loadLevelsFile.isEmpty()) 0579 { 0580 qCDebug(DIGIKAM_GENERAL_LOG) << "empty url"; 0581 return; 0582 } 0583 0584 QFile file(loadLevelsFile.toLocalFile()); 0585 0586 qCDebug(DIGIKAM_GENERAL_LOG) << "file path " << loadLevelsFile.toLocalFile(); 0587 0588 if (!file.open(QIODevice::ReadOnly)) 0589 { 0590 qCDebug(DIGIKAM_GENERAL_LOG) << "Cannot open file"; 0591 return; 0592 } 0593 0594 QXmlStreamReader xmlReader; 0595 xmlReader.setDevice(&file); 0596 0597 while (!xmlReader.atEnd()) 0598 { 0599 if (xmlReader.isStartElement() && (xmlReader.name() == QLatin1String("Image"))) 0600 { 0601 // get all attributes and its value of a tag in attrs variable. 0602 0603 QXmlStreamAttributes attrs = xmlReader.attributes(); 0604 0605 // get value of each attribute from QXmlStreamAttributes 0606 0607 QStringView url = attrs.value(QLatin1String("url")); 0608 0609 if (url.isEmpty()) 0610 { 0611 xmlReader.readNext(); 0612 continue; 0613 } 0614 0615 QList<QUrl> urls; 0616 urls.append(QUrl(url.toString())); 0617 0618 //allow tools to append a new file 0619 0620 slotAddImages(urls); 0621 0622 // read tool Image custom attributes and children element 0623 0624 Q_EMIT signalXMLLoadImageElement(xmlReader); 0625 } 0626 else if (xmlReader.isStartElement() && (xmlReader.name() != QLatin1String("Images"))) 0627 { 0628 // unmanaged start element (it should be tools one) 0629 0630 Q_EMIT signalXMLCustomElements(xmlReader); 0631 } 0632 else if (xmlReader.isEndElement() && (xmlReader.name() == QLatin1String("Images"))) 0633 { 0634 // if EndElement is Images return 0635 0636 grp.writeEntry("Last Images List Path", loadLevelsFile.adjusted(QUrl::RemoveFilename).toLocalFile()); 0637 config->sync(); 0638 file.close(); 0639 return; 0640 } 0641 0642 xmlReader.readNext(); 0643 } 0644 0645 file.close(); 0646 } 0647 0648 void DItemsList::slotSaveItems() 0649 { 0650 KSharedConfigPtr config = KSharedConfig::openConfig(); 0651 KConfigGroup grp = config->group(objectName()); 0652 QUrl lastFileUrl = QUrl::fromLocalFile(grp.readEntry("Last Images List Path", 0653 QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation))); 0654 QUrl saveLevelsFile; 0655 saveLevelsFile = DFileDialog::getSaveFileUrl(this, 0656 i18nc("@title:window", "Select the Image File List to Save"), 0657 lastFileUrl, 0658 i18nc("@option", "All Files (*)")); 0659 0660 qCDebug(DIGIKAM_GENERAL_LOG) << "file url " << saveLevelsFile.toDisplayString(); 0661 0662 if (saveLevelsFile.isEmpty()) 0663 { 0664 qCDebug(DIGIKAM_GENERAL_LOG) << "empty url"; 0665 return; 0666 } 0667 0668 QFile file(saveLevelsFile.toLocalFile()); 0669 0670 if (!file.open(QIODevice::WriteOnly)) 0671 { 0672 qCDebug(DIGIKAM_GENERAL_LOG) << "Cannot open target file"; 0673 return; 0674 } 0675 0676 QXmlStreamWriter xmlWriter; 0677 xmlWriter.setDevice(&file); 0678 0679 xmlWriter.setAutoFormatting(true); 0680 xmlWriter.writeStartDocument(); 0681 0682 xmlWriter.writeStartElement(QLatin1String("Images")); 0683 0684 QTreeWidgetItemIterator it(listView()); 0685 0686 while (*it) 0687 { 0688 DItemsListViewItem* const lvItem = dynamic_cast<DItemsListViewItem*>(*it); 0689 0690 if (lvItem) 0691 { 0692 xmlWriter.writeStartElement(QLatin1String("Image")); 0693 0694 xmlWriter.writeAttribute(QLatin1String("url"), lvItem->url().toDisplayString()); 0695 0696 Q_EMIT signalXMLSaveItem(xmlWriter, listView()->indexFromItem(lvItem).row()); 0697 0698 xmlWriter.writeEndElement(); // Image 0699 } 0700 0701 ++it; 0702 } 0703 0704 Q_EMIT signalXMLCustomElements(xmlWriter); 0705 0706 xmlWriter.writeEndElement(); // Images 0707 0708 xmlWriter.writeEndDocument(); // end document 0709 0710 grp.writeEntry("Last Images List Path", saveLevelsFile.adjusted(QUrl::RemoveFilename).toLocalFile()); 0711 config->sync(); 0712 file.close(); 0713 } 0714 0715 void DItemsList::removeItemByUrl(const QUrl& url) 0716 { 0717 bool found; 0718 QList<int> itemsIndex; 0719 0720 do 0721 { 0722 found = false; 0723 QTreeWidgetItemIterator it(d->listView); 0724 0725 while (*it) 0726 { 0727 DItemsListViewItem* const item = dynamic_cast<DItemsListViewItem*>(*it); 0728 0729 if (item && (item->url() == url)) 0730 { 0731 itemsIndex.append(d->listView->indexFromItem(item).row()); 0732 0733 if (d->processItems.contains(item->url())) 0734 { 0735 d->processItems.removeAll(item->url()); 0736 } 0737 0738 delete item; 0739 found = true; 0740 break; 0741 } 0742 0743 ++it; 0744 } 0745 } 0746 while (found); 0747 0748 Q_EMIT signalRemovedItems(itemsIndex); 0749 Q_EMIT signalImageListChanged(); 0750 } 0751 0752 QList<QUrl> DItemsList::imageUrls(bool onlyUnprocessed) const 0753 { 0754 QList<QUrl> list; 0755 QTreeWidgetItemIterator it(d->listView); 0756 0757 while (*it) 0758 { 0759 DItemsListViewItem* const item = dynamic_cast<DItemsListViewItem*>(*it); 0760 0761 if (item) 0762 { 0763 if ((onlyUnprocessed == false) || (item->state() != DItemsListViewItem::Success)) 0764 { 0765 list.append(item->url()); 0766 } 0767 } 0768 0769 ++it; 0770 } 0771 0772 return list; 0773 } 0774 0775 void DItemsList::slotProgressTimerDone() 0776 { 0777 if (!d->processItems.isEmpty()) 0778 { 0779 Q_FOREACH (const QUrl& url, d->processItems) 0780 { 0781 DItemsListViewItem* const item = listView()->findItem(url); 0782 0783 if (item) 0784 { 0785 item->setProgressAnimation(d->progressPix->frameAt(d->progressCount)); 0786 } 0787 } 0788 0789 d->progressCount++; 0790 0791 if (d->progressCount == 8) 0792 { 0793 d->progressCount = 0; 0794 } 0795 0796 d->progressTimer->start(300); 0797 } 0798 } 0799 0800 void DItemsList::processing(const QUrl& url) 0801 { 0802 DItemsListViewItem* const item = listView()->findItem(url); 0803 0804 if (item) 0805 { 0806 d->processItems.append(url); 0807 d->listView->setCurrentItem(item, true); 0808 d->listView->scrollToItem(item); 0809 d->progressTimer->start(300); 0810 } 0811 } 0812 0813 void DItemsList::processed(const QUrl& url, bool success) 0814 { 0815 DItemsListViewItem* const item = listView()->findItem(url); 0816 0817 if (item) 0818 { 0819 d->processItems.removeAll(url); 0820 item->setProcessedIcon(QIcon::fromTheme(success ? QLatin1String("dialog-ok-apply") 0821 : QLatin1String("dialog-cancel")).pixmap(16, 16)); 0822 item->setState(success ? DItemsListViewItem::Success 0823 : DItemsListViewItem::Failed); 0824 0825 if (d->processItems.isEmpty()) 0826 { 0827 d->progressTimer->stop(); 0828 } 0829 } 0830 } 0831 0832 void DItemsList::cancelProcess() 0833 { 0834 Q_FOREACH (const QUrl& url, d->processItems) 0835 { 0836 processed(url, false); 0837 } 0838 } 0839 0840 void DItemsList::clearProcessedStatus() 0841 { 0842 QTreeWidgetItemIterator it(d->listView); 0843 0844 while (*it) 0845 { 0846 DItemsListViewItem* const lvItem = dynamic_cast<DItemsListViewItem*>(*it); 0847 0848 if (lvItem) 0849 { 0850 lvItem->setProcessedIcon(QIcon()); 0851 } 0852 0853 ++it; 0854 } 0855 } 0856 0857 DItemsListView* DItemsList::listView() const 0858 { 0859 return d->listView; 0860 } 0861 0862 void DItemsList::slotImageListChanged() 0863 { 0864 const QList<QTreeWidgetItem*> selectedItemsList = d->listView->selectedItems(); 0865 const bool haveImages = !(imageUrls().isEmpty()) && d->controlButtonsEnabled; 0866 const bool haveSelectedImages = !(selectedItemsList.isEmpty()) && d->controlButtonsEnabled; 0867 const bool haveOnlyOneSelectedImage = (selectedItemsList.count() == 1) && d->controlButtonsEnabled; 0868 0869 d->removeButton->setEnabled(haveSelectedImages); 0870 d->moveUpButton->setEnabled(haveOnlyOneSelectedImage); 0871 d->moveDownButton->setEnabled(haveOnlyOneSelectedImage); 0872 d->clearButton->setEnabled(haveImages); 0873 0874 // All buttons are enabled / disabled now, but the "Add" button should always be 0875 // enabled, if the buttons are not explicitly disabled with enableControlButtons() 0876 0877 d->addButton->setEnabled(d->controlButtonsEnabled); 0878 0879 // TODO: should they be enabled by default now? 0880 0881 d->loadButton->setEnabled(d->controlButtonsEnabled); 0882 d->saveButton->setEnabled(d->controlButtonsEnabled); 0883 } 0884 0885 void DItemsList::updateThumbnail(const QUrl& url) 0886 { 0887 d->thumbLoadThread->find(ThumbnailIdentifier(url.toLocalFile())); 0888 } 0889 0890 void DItemsList::slotThumbnail(const LoadingDescription& desc, const QPixmap& pix) 0891 { 0892 QTreeWidgetItemIterator it(d->listView); 0893 0894 while (*it) 0895 { 0896 DItemsListViewItem* const item = dynamic_cast<DItemsListViewItem*>(*it); 0897 0898 if (item && (item->url() == QUrl::fromLocalFile(desc.filePath))) 0899 { 0900 if (!pix.isNull()) 0901 { 0902 item->setThumb(pix.scaled(d->iconSize, d->iconSize, Qt::KeepAspectRatio)); 0903 } 0904 0905 if (!d->allowDuplicate) 0906 { 0907 return; 0908 } 0909 } 0910 0911 ++it; 0912 } 0913 } 0914 0915 DItemsListViewItem* DItemsListView::getCurrentItem() const 0916 { 0917 QTreeWidgetItem* const currentTreeItem = currentItem(); 0918 0919 if (!currentTreeItem) 0920 { 0921 return nullptr; 0922 } 0923 0924 return dynamic_cast<DItemsListViewItem*>(currentTreeItem); 0925 } 0926 0927 QUrl DItemsList::getCurrentUrl() const 0928 { 0929 DItemsListViewItem* const currentItem = d->listView->getCurrentItem(); 0930 0931 if (!currentItem) 0932 { 0933 return QUrl(); 0934 } 0935 0936 return currentItem->url(); 0937 } 0938 0939 void DItemsList::setCurrentUrl(const QUrl& url) 0940 { 0941 QTreeWidgetItemIterator it(d->listView); 0942 0943 while (*it) 0944 { 0945 DItemsListViewItem* const item = dynamic_cast<DItemsListViewItem*>(*it); 0946 0947 if (item && (item->url() == url)) 0948 { 0949 d->listView->setCurrentItem(item); 0950 return; 0951 } 0952 0953 ++it; 0954 } 0955 } 0956 0957 void DItemsList::setIsLessThanHandler(DItemsListIsLessThanHandler fncptr) 0958 { 0959 d->isLessThan = fncptr; 0960 } 0961 0962 DItemsListIsLessThanHandler DItemsList::isLessThanHandler() const 0963 { 0964 return d->isLessThan; 0965 } 0966 0967 } // namespace Digikam 0968 0969 #include "moc_ditemslist.cpp" 0970 0971 #include "moc_ditemslist_p.cpp"