File indexing completed on 2025-01-19 03:52:37
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2022-08-26 0007 * Description : Text converter batch dialog 0008 * 0009 * SPDX-FileCopyrightText: 2008-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2022 by Quoc Hung Tran <quochungtran1999 at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "textconverterdialog.h" 0017 0018 // C++ includes 0019 0020 #include <sstream> 0021 0022 // Qt includes 0023 0024 #include <QGridLayout> 0025 #include <QTimer> 0026 #include <QStyle> 0027 #include <QApplication> 0028 #include <QMessageBox> 0029 #include <QTabWidget> 0030 #include <QMenu> 0031 #include <QScrollArea> 0032 0033 // KDE includes 0034 0035 #include <klocalizedstring.h> 0036 0037 // Local includes 0038 0039 #include "digikam_debug.h" 0040 #include "digikam_config.h" 0041 #include "dcombobox.h" 0042 #include "dprogresswdg.h" 0043 #include "textconverterlist.h" 0044 #include "textconvertersettings.h" 0045 #include "ocroptions.h" 0046 #include "dtextedit.h" 0047 #include "dlayoutbox.h" 0048 #include "textconverterthread.h" 0049 #include "textconverteraction.h" 0050 #include "tesseractbinary.h" 0051 #include "dbinarysearch.h" 0052 0053 using namespace Digikam; 0054 0055 namespace DigikamGenericTextConverterPlugin 0056 { 0057 0058 class TextConverterDialog::Private 0059 { 0060 public: 0061 0062 enum OcrTabs 0063 { 0064 RecognitionTab = 0, 0065 ReviewTab 0066 }; 0067 0068 enum ProcessActions 0069 { 0070 ProcessAll = 0, 0071 ProcessSelected 0072 }; 0073 0074 public: 0075 0076 Private() 0077 : busy (false), 0078 progressBar (nullptr), 0079 thread (nullptr), 0080 iface (nullptr), 0081 listView (nullptr), 0082 ocrSettings (nullptr), 0083 textedit (nullptr), 0084 saveTextButton (nullptr), 0085 currentSelectedItem (nullptr), 0086 binWidget (nullptr), 0087 tabView (nullptr) 0088 { 0089 } 0090 0091 bool busy; 0092 0093 QList<QUrl> fileList; 0094 0095 QMap<QUrl, QString> textEditList; 0096 0097 DProgressWdg* progressBar; 0098 0099 TextConverterActionThread* thread; 0100 0101 DInfoInterface* iface; 0102 0103 TextConverterList* listView; 0104 0105 TextConverterSettings* ocrSettings; 0106 0107 DTextEdit* textedit; 0108 0109 QPushButton* saveTextButton; 0110 0111 TextConverterListViewItem* currentSelectedItem; 0112 0113 TesseractBinary tesseractBin; 0114 DBinarySearch* binWidget; 0115 0116 QTabWidget* tabView; 0117 }; 0118 0119 TextConverterDialog::TextConverterDialog(QWidget* const parent, DInfoInterface* const iface) 0120 : DPluginDialog(parent, QLatin1String("Text Converter Dialog")), 0121 d (new Private) 0122 { 0123 setWindowTitle(i18nc("@title:window", "OCR Text Converter")); 0124 setModal(true); 0125 0126 d->iface = iface; 0127 0128 const int spacing = qMin(QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0129 QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing)); 0130 0131 m_buttons->addButton(QDialogButtonBox::Close); 0132 m_buttons->addButton(QDialogButtonBox::Ok); 0133 m_buttons->button(QDialogButtonBox::Ok)->setText(i18nc("@action:button", "&Start OCR")); 0134 m_buttons->button(QDialogButtonBox::Ok)->setDisabled(true); 0135 m_buttons->addButton(QDialogButtonBox::Reset); 0136 m_buttons->button(QDialogButtonBox::Reset)->setText(i18nc("@action: button", "&Default")); 0137 m_buttons->button(QDialogButtonBox::Reset)->setIcon(QIcon::fromTheme(QLatin1String("document-revert"))); 0138 m_buttons->button(QDialogButtonBox::Reset)->setToolTip(i18nc("@info:tooltip", 0139 "Revert current settings to default values.")); 0140 0141 QWidget* const mainWidget = new QWidget(this); 0142 QVBoxLayout* const vbx = new QVBoxLayout(this); 0143 vbx->addWidget(mainWidget); 0144 vbx->addWidget(m_buttons); 0145 setLayout(vbx); 0146 0147 //------------------------------------------------------------------------------------------- 0148 0149 QGridLayout* const mainLayout = new QGridLayout(mainWidget); 0150 d->listView = new TextConverterList(mainWidget); 0151 d->progressBar = new DProgressWdg(mainWidget); 0152 d->progressBar->reset(); 0153 d->progressBar->setVisible(false); 0154 0155 d->listView->appendControlButtonsWidget(d->progressBar); 0156 QBoxLayout* const blay = d->listView->setControlButtonsPlacement(DItemsList::ControlButtonsBelow); 0157 blay->setStretchFactor(d->progressBar, 20); 0158 0159 d->tabView = new QTabWidget(mainWidget); 0160 0161 // -- Recognition tab 0162 0163 QScrollArea* const recsv = new QScrollArea(d->tabView); 0164 DVBox* const recognitionTab = new DVBox(recsv->viewport()); 0165 QLabel* const tesseractLabel = new QLabel(i18nc("@label", "This tool use the %1 open-source " 0166 "engine to perform Optical Characters Recognition. " 0167 "Tesseract program and the desired languages modules must " 0168 "be installed on your system.", 0169 QString::fromUtf8("<a href='https://github.com/tesseract-ocr/tesseract'>Tesseract</a>")), 0170 recognitionTab); 0171 tesseractLabel->setWordWrap(true); 0172 tesseractLabel->setOpenExternalLinks(true); 0173 0174 d->binWidget = new DBinarySearch(recognitionTab); 0175 d->binWidget->addBinary(d->tesseractBin); 0176 0177 #ifdef Q_OS_MACOS 0178 0179 // Std Macports install 0180 0181 d->binWidget->addDirectory(QLatin1String("/opt/local/bin")); 0182 0183 // digiKam Bundle PKG install 0184 0185 d->binWidget->addDirectory(macOSBundlePrefix() + QLatin1String("bin")); 0186 0187 #endif 0188 0189 #ifdef Q_OS_WIN 0190 0191 d->binWidget->addDirectory(QLatin1String("C:/Program Files/Tesseract-OCR")); 0192 0193 #endif 0194 0195 #ifdef Q_OS_UNIX 0196 0197 d->binWidget->addDirectory(QLatin1String("/usr/bin")); 0198 d->binWidget->addDirectory(QLatin1String("/usr/local/bin")); 0199 d->binWidget->addDirectory(QLatin1String("/bin")); 0200 0201 #endif 0202 0203 d->ocrSettings = new TextConverterSettings(recognitionTab); 0204 0205 0206 recognitionTab->setContentsMargins(spacing, spacing, spacing, spacing); 0207 recognitionTab->setSpacing(spacing); 0208 0209 recsv->setFrameStyle(QFrame::NoFrame); 0210 recsv->setWidgetResizable(true); 0211 recsv->setWidget(recognitionTab); 0212 0213 d->tabView->insertTab(Private::RecognitionTab, recsv, i18nc("@title", "Text Recognition")); 0214 0215 // --- Review tab -------------------------------------------------------------------------- 0216 0217 DVBox* const reviewTab = new DVBox(d->tabView); 0218 d->textedit = new DTextEdit(0, reviewTab); 0219 d->textedit->setPlaceholderText(i18nc("@info", "After to process recognition, " 0220 "double-click on one item to " 0221 "display recognized text here. " 0222 "You can review words and fix " 0223 "if necessary. Press the Save " 0224 "button to record your changes.")); 0225 reviewTab->setStretchFactor(d->textedit, 100); 0226 0227 d->saveTextButton = new QPushButton(reviewTab); 0228 d->saveTextButton->setText(i18nc("@action: button", "Save")); 0229 d->saveTextButton->setEnabled(false); 0230 0231 reviewTab->setContentsMargins(spacing, spacing, spacing, spacing); 0232 reviewTab->setSpacing(spacing); 0233 0234 d->tabView->insertTab(Private::ReviewTab, reviewTab, i18nc("@title", "Text Review")); 0235 0236 //------------------------------------------------------------------------------------------- 0237 0238 mainLayout->addWidget(d->listView, 0, 0, 1, 1); 0239 mainLayout->addWidget(d->tabView, 0, 1, 1, 1); 0240 mainLayout->setColumnStretch(0, 7); 0241 mainLayout->setColumnStretch(1, 3); 0242 mainLayout->setRowStretch(0, 10); 0243 mainLayout->setContentsMargins(QMargins()); 0244 0245 // --------------------------------------------------------------- 0246 0247 d->thread = new TextConverterActionThread(this); 0248 0249 connect(d->thread, SIGNAL(signalStarting(DigikamGenericTextConverterPlugin::TextConverterActionData)), 0250 this, SLOT(slotTextConverterAction(DigikamGenericTextConverterPlugin::TextConverterActionData))); 0251 0252 connect(d->thread, SIGNAL(signalFinished(DigikamGenericTextConverterPlugin::TextConverterActionData)), 0253 this, SLOT(slotTextConverterAction(DigikamGenericTextConverterPlugin::TextConverterActionData))); 0254 0255 connect(d->thread, SIGNAL(finished()), 0256 this, SLOT(slotThreadFinished())); 0257 0258 // --------------------------------------------------------------- 0259 0260 connect(m_buttons->button(QDialogButtonBox::Ok), SIGNAL(clicked()), 0261 this, SLOT(slotStartStop())); 0262 0263 connect(m_buttons->button(QDialogButtonBox::Close), SIGNAL(clicked()), 0264 this, SLOT(slotClose())); 0265 0266 connect(m_buttons->button(QDialogButtonBox::Reset), SIGNAL(clicked()), 0267 this, SLOT(slotDefault())); 0268 0269 connect(d->progressBar, SIGNAL(signalProgressCanceled()), 0270 this, SLOT(slotStartStop())); 0271 0272 connect(d->listView->listView(), &DItemsListView::itemDoubleClicked, 0273 this, &TextConverterDialog::slotDoubleClick); 0274 0275 connect(d->listView->listView(), &DItemsListView::itemSelectionChanged, 0276 this, &TextConverterDialog::slotSetDisable); 0277 0278 connect(d->saveTextButton, SIGNAL(clicked()), 0279 this, SLOT(slotUpdateText())); 0280 0281 connect(d->binWidget, SIGNAL(signalBinariesFound(bool)), 0282 this, SLOT(slotTesseractBinaryFound(bool))); 0283 0284 // --------------------------------------------------------------- 0285 0286 d->listView->setIface(d->iface); 0287 d->listView->loadImagesFromCurrentSelection(); 0288 0289 d->ocrSettings->readSettings(); 0290 0291 // --------------------------------------------------------------- 0292 0293 QTimer::singleShot(0, this, SLOT(slotStartFoundTesseract())); 0294 } 0295 0296 TextConverterDialog::~TextConverterDialog() 0297 { 0298 delete d; 0299 } 0300 0301 void TextConverterDialog::slotDoubleClick(QTreeWidgetItem* element) 0302 { 0303 TextConverterListViewItem* const item = dynamic_cast<TextConverterListViewItem*>(element); 0304 0305 if (!item) 0306 { 0307 d->currentSelectedItem = nullptr; 0308 return; 0309 } 0310 0311 d->currentSelectedItem = item; 0312 0313 if (d->textEditList.contains(d->currentSelectedItem->url())) 0314 { 0315 d->tabView->setCurrentIndex(Private::ReviewTab); 0316 d->textedit->setText(d->textEditList[d->currentSelectedItem->url()]); 0317 d->saveTextButton->setEnabled(true); 0318 } 0319 else 0320 { 0321 d->textedit->clear(); 0322 } 0323 } 0324 0325 void TextConverterDialog::slotUpdateText() 0326 { 0327 QString newText = d->textedit->text(); 0328 OcrOptions opt = d->ocrSettings->ocrOptions(); 0329 opt.tesseractPath = d->tesseractBin.path(); 0330 opt.iface = d->iface; 0331 0332 if (!d->textedit->text().isEmpty() && 0333 !d->currentSelectedItem->url().isEmpty() && 0334 !d->currentSelectedItem->destFileName().isEmpty()) 0335 { 0336 d->textEditList[d->currentSelectedItem->url()] = newText; 0337 d->currentSelectedItem->setRecognizedWords(QString::fromLatin1("%1").arg(calculateNumberOfWords(newText))); 0338 0339 MetaEngine::AltLangMap commentsMap; 0340 commentsMap.insert(QLatin1String("x-default"), newText); 0341 0342 if (opt.isSaveTextFile || opt.isSaveXMP) 0343 { 0344 OcrTesseractEngine::translate(commentsMap, opt.translations); 0345 } 0346 0347 if (opt.isSaveTextFile) 0348 { 0349 QString outFile = d->currentSelectedItem->destFileName(); 0350 OcrTesseractEngine::saveTextFile(d->currentSelectedItem->url().toLocalFile(), 0351 outFile, 0352 commentsMap); 0353 } 0354 0355 if (opt.isSaveXMP) 0356 { 0357 OcrTesseractEngine::saveXMP(d->currentSelectedItem->url(), 0358 commentsMap, 0359 opt.iface); 0360 } 0361 } 0362 } 0363 0364 void TextConverterDialog::slotSetDisable() 0365 { 0366 d->saveTextButton->setEnabled(false); 0367 } 0368 0369 void TextConverterDialog::slotTextConverterAction(const DigikamGenericTextConverterPlugin::TextConverterActionData& ad) 0370 { 0371 if (ad.starting) 0372 { 0373 switch (ad.action) 0374 { 0375 case PROCESS: 0376 { 0377 setBusy(true); 0378 d->listView->processing(ad.fileUrl); 0379 d->progressBar->progressStatusChanged(i18nc("@info", "Processing %1", ad.fileUrl.fileName())); 0380 break; 0381 } 0382 0383 default: 0384 { 0385 qCWarning(DIGIKAM_GENERAL_LOG) << "DigikamGenericTextConverterPlugin: Unknown action"; 0386 break; 0387 } 0388 } 0389 } 0390 else 0391 { 0392 if (ad.result != OcrTesseractEngine::PROCESS_COMPLETE) 0393 { 0394 switch (ad.action) 0395 { 0396 case PROCESS: 0397 { 0398 processingFailed(ad.fileUrl, ad.result); 0399 break; 0400 } 0401 0402 default: 0403 { 0404 qCWarning(DIGIKAM_GENERAL_LOG) << "DigikamGenericTextConverterPlugin: Unknown action"; 0405 break; 0406 } 0407 } 0408 } 0409 else // Something is done... 0410 { 0411 switch (ad.action) 0412 { 0413 case PROCESS: 0414 { 0415 d->textEditList[ad.fileUrl] = ad.outputText; 0416 processed(ad.fileUrl, ad.destPath, ad.outputText); 0417 0418 break; 0419 } 0420 0421 default: 0422 { 0423 qCWarning(DIGIKAM_GENERAL_LOG) << "DigikamGenericTextConverterPlugin: Unknown action"; 0424 0425 break; 0426 } 0427 } 0428 } 0429 } 0430 } 0431 0432 void TextConverterDialog::processingFailed(const QUrl& url, int result) 0433 { 0434 d->listView->processed(url, false); 0435 d->progressBar->setValue(d->progressBar->value()+1); 0436 0437 TextConverterListViewItem* const item = dynamic_cast<TextConverterListViewItem*>(d->listView->listView()->findItem(url)); 0438 0439 if (!item) 0440 { 0441 return; 0442 } 0443 0444 QString status; 0445 0446 switch (result) 0447 { 0448 case OcrTesseractEngine::PROCESS_FAILED: 0449 { 0450 status = i18nc("@info", "Process failed"); 0451 break; 0452 } 0453 0454 case OcrTesseractEngine::PROCESS_CANCELED: 0455 { 0456 status = i18nc("@info", "Process canceled"); 0457 break; 0458 } 0459 0460 // TODO Tesseract ocr error 0461 0462 default: 0463 { 0464 status = i18nc("@info", "Internal error"); 0465 break; 0466 } 0467 } 0468 0469 item->setStatus(status); 0470 } 0471 0472 int TextConverterDialog::calculateNumberOfWords(const QString& text) const 0473 { 0474 if (!text.isEmpty()) 0475 { 0476 std::stringstream ss; 0477 ss << text.toStdString(); 0478 0479 int count = 0; 0480 std::string word; 0481 0482 while (ss >> word) 0483 { 0484 if ((word.length() == 1) && std::ispunct(word[0])) 0485 { 0486 continue; 0487 } 0488 0489 count++; 0490 } 0491 0492 return count; 0493 } 0494 0495 return 0; 0496 } 0497 0498 void TextConverterDialog::processed(const QUrl& url, 0499 const QString& outputFile, 0500 const QString& ocrResult) 0501 { 0502 TextConverterListViewItem* const item = dynamic_cast<TextConverterListViewItem*>(d->listView->listView()->findItem(url)); 0503 0504 if (!item) 0505 { 0506 return; 0507 } 0508 0509 if (!outputFile.isEmpty()) 0510 { 0511 item->setDestFileName(outputFile); 0512 } 0513 0514 d->listView->processed(url, true); 0515 item->setStatus(i18nc("@info", "Success")); 0516 item->setRecognizedWords(QString::fromLatin1("%1").arg(calculateNumberOfWords(ocrResult))); 0517 d->progressBar->setValue(d->progressBar->value() + 1); 0518 } 0519 0520 void TextConverterDialog::processAll() 0521 { 0522 OcrOptions opt = d->ocrSettings->ocrOptions(); 0523 opt.tesseractPath = d->tesseractBin.path(); 0524 opt.iface = d->iface; 0525 d->thread->setOcrOptions(opt); 0526 d->thread->ocrProcessFiles(d->fileList); 0527 0528 if (!d->thread->isRunning()) 0529 { 0530 d->thread->start(); 0531 } 0532 } 0533 0534 void TextConverterDialog::slotStartStop() 0535 { 0536 if (!d->busy) 0537 { 0538 QAction* const ac = qobject_cast<QAction*>(sender()); 0539 0540 if (!ac) 0541 { 0542 return; 0543 } 0544 0545 int ptype = ac->data().toInt(); 0546 0547 d->fileList.clear(); 0548 0549 if (d->listView->listView()->topLevelItemCount() == 0) 0550 { 0551 d->textedit->clear(); 0552 } 0553 0554 QTreeWidgetItemIterator it(d->listView->listView()); 0555 0556 while (*it) 0557 { 0558 TextConverterListViewItem* const lvItem = dynamic_cast<TextConverterListViewItem*>(*it); 0559 0560 if (lvItem) 0561 { 0562 if ( 0563 !lvItem->isDisabled() && 0564 (lvItem->state() != TextConverterListViewItem::Success) && 0565 ((ptype == Private::ProcessAll) ? true : lvItem->isSelected()) 0566 ) 0567 { 0568 lvItem->setIcon(1, QIcon()); 0569 lvItem->setState(TextConverterListViewItem::Waiting); 0570 d->fileList.append(lvItem->url()); 0571 } 0572 } 0573 0574 ++it; 0575 } 0576 0577 if (d->fileList.empty()) 0578 { 0579 QMessageBox::information(this, i18nc("@title:window", "Text Converter"), 0580 i18nc("@info", "The list does not contain any digital files to process. " 0581 "You need to select them.")); 0582 setBusy(false); 0583 slotAborted(); 0584 0585 return; 0586 } 0587 0588 d->progressBar->setMaximum(d->fileList.count()); 0589 d->progressBar->setValue(0); 0590 d->progressBar->setVisible(true); 0591 d->progressBar->progressScheduled(i18nc("@title", "Text Converter"), true, true); 0592 d->progressBar->progressThumbnailChanged(QIcon::fromTheme(QLatin1String("text-x-generic")).pixmap(22, 22)); 0593 0594 processAll(); 0595 } 0596 else 0597 { 0598 d->fileList.clear(); 0599 d->thread->cancel(); 0600 setBusy(false); 0601 0602 d->listView->cancelProcess(); 0603 0604 QTimer::singleShot(500, this, SLOT(slotAborted())); 0605 } 0606 } 0607 0608 void TextConverterDialog::closeEvent(QCloseEvent* e) 0609 { 0610 if (!e) 0611 { 0612 return; 0613 } 0614 0615 // Stop current conversion if necessary 0616 0617 if (d->busy) 0618 { 0619 slotStartStop(); 0620 } 0621 0622 d->ocrSettings->saveSettings(); 0623 d->listView->listView()->clear(); 0624 e->accept(); 0625 } 0626 0627 void TextConverterDialog::slotClose() 0628 { 0629 // Stop current conversion if necessary 0630 0631 if (d->busy) 0632 { 0633 slotStartStop(); 0634 } 0635 0636 d->ocrSettings->saveSettings(); 0637 d->listView->listView()->clear(); 0638 d->fileList.clear(); 0639 accept(); 0640 } 0641 0642 void TextConverterDialog::slotDefault() 0643 { 0644 d->ocrSettings->setDefaultSettings(); 0645 } 0646 0647 void TextConverterDialog::addItems(const QList<QUrl>& itemList) 0648 { 0649 d->listView->slotAddImages(itemList); 0650 } 0651 0652 void TextConverterDialog::slotThreadFinished() 0653 { 0654 setBusy(false); 0655 slotAborted(); 0656 } 0657 0658 void TextConverterDialog::setBusy(bool busy) 0659 { 0660 d->busy = busy; 0661 0662 if (d->busy) 0663 { 0664 m_buttons->button(QDialogButtonBox::Ok)->setText(i18nc("@action", "&Abort")); 0665 m_buttons->button(QDialogButtonBox::Ok)->setToolTip(i18nc("@info", "Abort OCR processing of Raw files.")); 0666 unplugProcessMenu(); 0667 } 0668 else 0669 { 0670 m_buttons->button(QDialogButtonBox::Ok)->setText(i18nc("@action", "&Start OCR")); 0671 m_buttons->button(QDialogButtonBox::Ok)->setToolTip(i18nc("@info", "Start OCR using the current settings.")); 0672 plugProcessMenu(); 0673 } 0674 0675 d->ocrSettings->setEnabled(!d->busy); 0676 d->listView->listView()->viewport()->setEnabled(!d->busy); 0677 d->busy ? setCursor(Qt::WaitCursor) : unsetCursor(); 0678 } 0679 0680 void TextConverterDialog::slotAborted() 0681 { 0682 d->progressBar->setValue(0); 0683 d->progressBar->setVisible(false); 0684 d->progressBar->progressCompleted(); 0685 } 0686 0687 void TextConverterDialog::slotStartFoundTesseract() 0688 { 0689 bool b = d->binWidget->allBinariesFound(); 0690 slotTesseractBinaryFound(b); 0691 } 0692 0693 void TextConverterDialog::slotTesseractBinaryFound(bool found) 0694 { 0695 qCDebug(DIGIKAM_GENERAL_LOG) << "Tesseract binary found:" << found; 0696 0697 QStringList langs = d->tesseractBin.tesseractLanguages(); 0698 d->ocrSettings->populateLanguagesMode(langs); 0699 0700 bool b = found && !langs.isEmpty(); 0701 0702 setBusy(false); 0703 0704 // Disable Start button if Tesseract is not found or if no language plugin installed. 0705 0706 m_buttons->button(QDialogButtonBox::Ok)->setEnabled(b); 0707 0708 if (b) 0709 { 0710 m_buttons->button(QDialogButtonBox::Ok)->setToolTip(i18nc("@info", "Start OCR using the current settings.")); 0711 } 0712 else 0713 { 0714 m_buttons->button(QDialogButtonBox::Ok)->setToolTip(i18nc("@info", "Tesseract program or language modules\n" 0715 "are not installed on your system.")); 0716 } 0717 } 0718 0719 void TextConverterDialog::plugProcessMenu() 0720 { 0721 if (!m_buttons->button(QDialogButtonBox::Ok)->menu()) 0722 { 0723 QMenu* const processMenu = new QMenu(this); 0724 m_buttons->button(QDialogButtonBox::Ok)->setMenu(processMenu); 0725 0726 connect(processMenu, SIGNAL(aboutToShow()), 0727 this, SLOT(slotProcessMenu())); 0728 } 0729 } 0730 0731 void TextConverterDialog::unplugProcessMenu() 0732 { 0733 m_buttons->button(QDialogButtonBox::Ok)->setMenu(nullptr); 0734 } 0735 0736 void TextConverterDialog::slotProcessMenu() 0737 { 0738 if (!d->busy) 0739 { 0740 QMenu* const processMenu = qobject_cast<QMenu*>(sender()); 0741 0742 if (processMenu) 0743 { 0744 processMenu->clear(); 0745 0746 QAction* ac = nullptr; 0747 ac = processMenu->addAction(i18nc("@action", "Process All Items"), this, SLOT(slotStartStop())); 0748 ac->setData(Private::ProcessAll); 0749 ac = processMenu->addAction(i18nc("@action", "Process Selected Items"), this, SLOT(slotStartStop())); 0750 ac->setData(Private::ProcessSelected); 0751 } 0752 } 0753 } 0754 0755 } // namespace DigikamGenericTextConverterPlugin 0756 0757 #include "moc_textconverterdialog.cpp"