File indexing completed on 2024-12-22 04:28:23

0001 /*
0002   SPDX-FileCopyrightText: 2023-2024 Laurent Montel <montel.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "voskenginelanguagewidget.h"
0008 #include "libvoskspeechtotext_debug.h"
0009 #include "voskspeechtotextmodel.h"
0010 #include "voskspeechtotextproxymodel.h"
0011 
0012 #include <KLocalizedString>
0013 #include <KMessageBox>
0014 #include <QLabel>
0015 #include <QLineEdit>
0016 #include <QProgressBar>
0017 #include <QPushButton>
0018 #include <QTreeView>
0019 #include <QVBoxLayout>
0020 
0021 #if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
0022 #include <TextAddonsWidgets/LineEditCatchReturnKey>
0023 #else
0024 #include <KLineEditEventHandler>
0025 #endif
0026 
0027 VoskEngineLanguageWidget::VoskEngineLanguageWidget(QWidget *parent)
0028     : QWidget{parent}
0029     , mTreeView(new QTreeView(this))
0030     , mSearchLineEdit(new QLineEdit(this))
0031     , mVoskSpeechToTextModel(new VoskSpeechToTextModel(this))
0032     , mVoskSpeechToTextProxyModel(new VoskSpeechToTextProxyModel(this))
0033     , mProgressBar(new QProgressBar(this))
0034     , mProgressBarLabel(new QLabel(this))
0035     , mProgressBarWidget(new QWidget(this))
0036 {
0037     auto mainLayout = new QVBoxLayout(this);
0038     mainLayout->setObjectName(QStringLiteral("mainLayout"));
0039     mainLayout->setContentsMargins({});
0040 
0041     auto hboxLayout = new QHBoxLayout;
0042     hboxLayout->setObjectName(QStringLiteral("hboxLayout"));
0043     hboxLayout->setContentsMargins({});
0044     mainLayout->addLayout(hboxLayout);
0045 
0046     auto vboxLayout = new QVBoxLayout;
0047     vboxLayout->setObjectName(QStringLiteral("vboxLayout"));
0048     vboxLayout->setContentsMargins({});
0049 
0050     mSearchLineEdit->setObjectName(QStringLiteral("mSearchLineEdit"));
0051     vboxLayout->addWidget(mSearchLineEdit);
0052     mSearchLineEdit->setPlaceholderText(i18n("Search..."));
0053     mSearchLineEdit->setClearButtonEnabled(true);
0054 
0055 #if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
0056     new TextAddonsWidgets::LineEditCatchReturnKey(mSearchLineEdit, this);
0057 #else
0058     KLineEditEventHandler::catchReturnKey(mSearchLineEdit);
0059 #endif
0060 
0061     mTreeView->setObjectName(QStringLiteral("mTreeView"));
0062     connect(ManagerModelVoskSpeechToText::self(), &ManagerModelVoskSpeechToText::errorText, this, &VoskEngineLanguageWidget::slotError);
0063     connect(ManagerModelVoskSpeechToText::self(), &ManagerModelVoskSpeechToText::progress, this, &VoskEngineLanguageWidget::slotProgressInfo);
0064     connect(ManagerModelVoskSpeechToText::self(),
0065             &ManagerModelVoskSpeechToText::extractDone,
0066             mVoskSpeechToTextModel,
0067             &VoskSpeechToTextModel::updateInstalledLanguage);
0068     connect(ManagerModelVoskSpeechToText::self(), &ManagerModelVoskSpeechToText::downLoadModelListDone, this, &VoskEngineLanguageWidget::updateListModel);
0069 
0070     // TODO store list on local.
0071     if (ManagerModelVoskSpeechToText::self()->needDownloadModelList()) {
0072         ManagerModelVoskSpeechToText::self()->downloadListModels();
0073     } else {
0074         mVoskSpeechToTextModel->setSpeechToTextInfos(ManagerModelVoskSpeechToText::self()->speechToTextInfos());
0075     }
0076 
0077     mVoskSpeechToTextProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
0078     mVoskSpeechToTextProxyModel->setSourceModel(mVoskSpeechToTextModel);
0079     mTreeView->setModel(mVoskSpeechToTextProxyModel);
0080     mTreeView->setRootIsDecorated(false);
0081     mTreeView->setSortingEnabled(true);
0082     vboxLayout->addWidget(mTreeView);
0083 
0084     hboxLayout->addLayout(vboxLayout);
0085 
0086     auto buttonLayout = new QVBoxLayout;
0087     buttonLayout->setObjectName(QStringLiteral("buttonLayout"));
0088     hboxLayout->addLayout(buttonLayout);
0089 
0090     auto downLoadLanguage = new QPushButton(QIcon::fromTheme(QStringLiteral("download-symbolic")), i18n("Download"), this);
0091     downLoadLanguage->setObjectName(QStringLiteral("downLoadLanguage"));
0092     buttonLayout->addWidget(downLoadLanguage);
0093     connect(downLoadLanguage, &QPushButton::clicked, this, [this]() {
0094         const auto currentlySelectedIndex = mVoskSpeechToTextProxyModel->mapToSource(mTreeView->selectionModel()->currentIndex());
0095         const QModelIndex modelIndex = mVoskSpeechToTextModel->index(currentlySelectedIndex.row(), VoskSpeechToTextModel::Url);
0096         const QModelIndex modelIndexCheckSum = mVoskSpeechToTextModel->index(currentlySelectedIndex.row(), VoskSpeechToTextModel::CheckSum);
0097         const QModelIndex modelIndexName = mVoskSpeechToTextModel->index(currentlySelectedIndex.row(), VoskSpeechToTextModel::Name);
0098         const QModelIndex modelIndexVersion = mVoskSpeechToTextModel->index(currentlySelectedIndex.row(), VoskSpeechToTextModel::AvailableVersion);
0099 
0100         const QString url = modelIndex.data().toString();
0101         const QString checkSum = modelIndexCheckSum.data().toString();
0102         qCDebug(LIBVOSKSPEECHTOTEXT_LOG) << " url " << url << " checksum " << checkSum;
0103         VoskDownloadLanguageJob::DownloadLanguageInfo info;
0104         info.checksum = modelIndexCheckSum.data().toString();
0105         info.url = QUrl(modelIndex.data().toString());
0106         info.name = modelIndexName.data().toString();
0107         info.version = modelIndexVersion.data().toString();
0108         slotDownLoad(info);
0109     });
0110 
0111     auto deleteLanguage = new QPushButton(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("Delete"), this);
0112     deleteLanguage->setObjectName(QStringLiteral("downLoadLanguage"));
0113     buttonLayout->addWidget(deleteLanguage);
0114 
0115     auto updateListLanguage = new QPushButton(i18n("Update List"), this);
0116     updateListLanguage->setToolTip(i18n("Update list of languages from network"));
0117     updateListLanguage->setObjectName(QStringLiteral("updateListLanguage"));
0118     buttonLayout->addWidget(updateListLanguage);
0119     buttonLayout->addStretch(1); // spacer
0120     connect(updateListLanguage, &QPushButton::clicked, this, &VoskEngineLanguageWidget::slotUpdateListLanguage);
0121 
0122     connect(mSearchLineEdit, &QLineEdit::textChanged, this, &VoskEngineLanguageWidget::slotTextChanged);
0123 
0124     auto progressBarLayout = new QHBoxLayout;
0125     progressBarLayout->setObjectName(QStringLiteral("progressBarLayout"));
0126 
0127     mProgressBarLabel->setObjectName(QStringLiteral("mProgressBarLabel"));
0128     progressBarLayout->addWidget(mProgressBarLabel);
0129 
0130     mProgressBar->setObjectName(QStringLiteral("mProgressBar"));
0131     progressBarLayout->addWidget(mProgressBar);
0132 
0133     mProgressBarWidget->setObjectName(QStringLiteral("mProgressBarWidget"));
0134     mProgressBarWidget->setLayout(progressBarLayout);
0135 
0136     mainLayout->addWidget(mProgressBarWidget);
0137     mProgressBarWidget->setVisible(false);
0138 
0139     mTreeView->setColumnHidden(VoskSpeechToTextModel::Url, true);
0140     mTreeView->setColumnHidden(VoskSpeechToTextModel::CheckSum, true);
0141     mTreeView->setColumnHidden(VoskSpeechToTextModel::Identifier, true);
0142     mTreeView->setColumnHidden(VoskSpeechToTextModel::NeedToUpdateLanguage, true);
0143     mTreeView->setColumnHidden(VoskSpeechToTextModel::Name, true);
0144     mTreeView->setColumnHidden(VoskSpeechToTextModel::SizeByte, true);
0145     auto updateButton = [this, downLoadLanguage, deleteLanguage]() {
0146         const bool hasSelectedItem = mTreeView->currentIndex().isValid();
0147         const auto currentlySelectedIndex = mVoskSpeechToTextProxyModel->mapToSource(mTreeView->selectionModel()->currentIndex());
0148         const QModelIndex modelIndex = mVoskSpeechToTextModel->index(currentlySelectedIndex.row(), VoskSpeechToTextModel::Installed);
0149         const QModelIndex modelIndexNeedToUpdate = mVoskSpeechToTextModel->index(currentlySelectedIndex.row(), VoskSpeechToTextModel::NeedToUpdateLanguage);
0150         const bool isInstalled = modelIndex.data().toBool();
0151         const bool needToUpdate = modelIndexNeedToUpdate.data().toBool();
0152 
0153         if (needToUpdate) {
0154             downLoadLanguage->setEnabled(hasSelectedItem && needToUpdate);
0155         } else {
0156             downLoadLanguage->setEnabled(hasSelectedItem && !isInstalled);
0157         }
0158         deleteLanguage->setEnabled(hasSelectedItem && isInstalled);
0159     };
0160 
0161     connect(deleteLanguage, &QPushButton::clicked, this, [this, updateButton]() {
0162         const auto currentlySelectedIndex = mVoskSpeechToTextProxyModel->mapToSource(mTreeView->selectionModel()->currentIndex());
0163         const QModelIndex modelIndex = mVoskSpeechToTextModel->index(currentlySelectedIndex.row(), VoskSpeechToTextModel::Name);
0164         const QString identifier = modelIndex.data().toString();
0165         slotDelete(identifier);
0166         updateButton();
0167     });
0168 
0169     connect(mTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [updateButton]() {
0170         updateButton();
0171     });
0172     downLoadLanguage->setEnabled(false);
0173     deleteLanguage->setEnabled(false);
0174 }
0175 
0176 VoskEngineLanguageWidget::~VoskEngineLanguageWidget() = default;
0177 
0178 void VoskEngineLanguageWidget::slotProgressInfo(const ManagerModelVoskSpeechToText::ProgressInfo &info)
0179 {
0180     if (info.bytesReceived != info.bytesTotal) {
0181         mProgressBarWidget->setVisible(true);
0182     } else {
0183         mProgressBarWidget->setVisible(false);
0184     }
0185     mProgressBarLabel->setText(info.languageName);
0186     mProgressBar->setRange(0, 100);
0187     mProgressBar->setValue((info.bytesReceived * 100) / info.bytesTotal);
0188 }
0189 
0190 void VoskEngineLanguageWidget::slotError(const QString &str)
0191 {
0192     KMessageBox::error(this, i18n("Error: %1", str), i18n("Vosk"));
0193 }
0194 
0195 void VoskEngineLanguageWidget::slotTextChanged(const QString &str)
0196 {
0197     mVoskSpeechToTextProxyModel->setSearchString(str);
0198 }
0199 
0200 void VoskEngineLanguageWidget::slotDownLoad(const VoskDownloadLanguageJob::DownloadLanguageInfo &info)
0201 {
0202     ManagerModelVoskSpeechToText::self()->downloadLanguage(info);
0203 }
0204 
0205 void VoskEngineLanguageWidget::slotDelete(const QString &name)
0206 {
0207     mVoskSpeechToTextModel->removeLanguage(name);
0208 }
0209 
0210 void VoskEngineLanguageWidget::slotUpdateListLanguage()
0211 {
0212     ManagerModelVoskSpeechToText::self()->downloadListModels();
0213 }
0214 
0215 void VoskEngineLanguageWidget::updateListModel()
0216 {
0217     mVoskSpeechToTextModel->setSpeechToTextInfos(ManagerModelVoskSpeechToText::self()->speechToTextInfos());
0218 }
0219 
0220 #include "moc_voskenginelanguagewidget.cpp"