File indexing completed on 2023-05-30 10:39:36
0001 /*************************************************************************** 0002 * Copyright (C) 2002 by Gunnar Schmi Dt <kmouth@schmi-dt.de * 0003 * (C) 2015 by Jeremy Whiting <jpwhiting@kde.org> * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; if not, write to the * 0017 * Free Software Foundation, Inc., * 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 0021 // application specific includes 0022 #include "phraselist.h" 0023 0024 // include files for Qt 0025 #include <QApplication> 0026 #include <QClipboard> 0027 #include <QFileDialog> 0028 #include <QHBoxLayout> 0029 #include <QKeyEvent> 0030 #include <QListView> 0031 #include <QPushButton> 0032 #include <QStandardItem> 0033 #include <QVBoxLayout> 0034 0035 // include files for KDE 0036 #include <KComboBox> 0037 #include <KConfigGroup> 0038 #include <QIcon> 0039 #include <KLineEdit> 0040 #include <KLocalizedString> 0041 #include <KMessageBox> 0042 #include <KXMLGUIFactory> 0043 0044 #include "kmouth.h" 0045 #include "texttospeechsystem.h" 0046 #include "phrasebook/phrasebook.h" 0047 #include "wordcompletion/wordcompletion.h" 0048 0049 PhraseList::PhraseList(QWidget *parent, const QString &name) : QWidget(parent) 0050 { 0051 Q_UNUSED(name); 0052 isInSlot = false; 0053 // FIXME: Remove or change PaletteBase to Qt::OpaqueMode? 0054 // setBackgroundMode(PaletteBase); 0055 QVBoxLayout *layout = new QVBoxLayout(this); 0056 0057 m_listView = new QListView(this); 0058 m_model = new QStandardItemModel(this); 0059 m_listView->setModel(m_model); 0060 m_listView->setFocusPolicy(Qt::NoFocus); 0061 m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection); 0062 m_listView->setWhatsThis(i18n("This list contains the history of spoken sentences. You can select sentences and press the speak button for re-speaking.")); 0063 layout->addWidget(m_listView); 0064 0065 QHBoxLayout *rowLayout = new QHBoxLayout(); 0066 layout->addLayout(rowLayout); 0067 0068 completion = new WordCompletion(); 0069 0070 dictionaryCombo = new KComboBox(this); 0071 configureCompletionCombo(completion->wordLists()); 0072 rowLayout->addWidget(dictionaryCombo); 0073 0074 lineEdit = new KLineEdit(this); 0075 lineEdit->setFocusPolicy(Qt::StrongFocus); 0076 lineEdit->setFrame(true); 0077 lineEdit->setEchoMode(QLineEdit::Normal); 0078 lineEdit->setCompletionObject(completion); 0079 lineEdit->setAutoDeleteCompletionObject(true); 0080 lineEdit->setWhatsThis(i18n("Into this edit field you can type a phrase. Click on the speak button in order to speak the entered phrase.")); 0081 rowLayout->addWidget(lineEdit); 0082 lineEdit->setFocus(); 0083 0084 QIcon icon = QIcon::fromTheme(QStringLiteral("text-speak")); 0085 speakButton = new QPushButton(icon, i18n("&Speak"), this); 0086 speakButton->setFocusPolicy(Qt::NoFocus); 0087 speakButton->setAutoDefault(false); 0088 speakButton->setWhatsThis(i18n("Speaks the currently active sentence(s). If there is some text in the edit field it is spoken. Otherwise the selected sentences in the history (if any) are spoken.")); 0089 rowLayout->addWidget(speakButton); 0090 0091 connect(dictionaryCombo, QOverload<const QString &>::of(&KComboBox::textActivated), completion, &WordCompletion::setWordList); 0092 connect(completion, &WordCompletion::wordListsChanged, this, &PhraseList::configureCompletionCombo); 0093 0094 connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PhraseList::selectionChanged); 0095 connect(m_listView, &QWidget::customContextMenuRequested, this, &PhraseList::contextMenuRequested); 0096 connect(lineEdit, &KLineEdit::returnKeyPressed, this, &PhraseList::lineEntered); 0097 connect(lineEdit, &QLineEdit::textChanged, this, &PhraseList::textChanged); 0098 connect(speakButton, &QAbstractButton::clicked, this, &PhraseList::speak); 0099 } 0100 0101 PhraseList::~PhraseList() 0102 { 0103 delete speakButton; 0104 delete m_model; 0105 delete lineEdit; 0106 } 0107 0108 void PhraseList::print(QPrinter *pPrinter) 0109 { 0110 PhraseBook book; 0111 QStandardItem *rootItem = m_model->invisibleRootItem(); 0112 int count = rootItem->rowCount(); 0113 for (int i = 0; i < count; ++i) { 0114 QStandardItem *item = rootItem->child(i); 0115 book += PhraseBookEntry(Phrase(item->text())); 0116 } 0117 0118 book.print(pPrinter); 0119 } 0120 0121 QStringList PhraseList::getListSelection() 0122 { 0123 QStringList res = QStringList(); 0124 0125 QStandardItem *rootItem = m_model->invisibleRootItem(); 0126 int count = rootItem->rowCount(); 0127 QItemSelectionModel *selection = m_listView->selectionModel(); 0128 for (int i = 0; i < count; ++i) { 0129 QStandardItem *item = rootItem->child(i); 0130 if (selection->isSelected(m_model->indexFromItem(item))) 0131 res += item->text(); 0132 } 0133 0134 return res; 0135 } 0136 0137 bool PhraseList::existListSelection() 0138 { 0139 return m_listView->selectionModel()->hasSelection(); 0140 } 0141 0142 bool PhraseList::existEditSelection() 0143 { 0144 return lineEdit->hasSelectedText(); 0145 } 0146 0147 void PhraseList::enableMenuEntries() 0148 { 0149 bool deselected = false; 0150 bool selected = existListSelection(); 0151 QStandardItem *rootItem = m_model->invisibleRootItem(); 0152 int count = rootItem->rowCount(); 0153 QItemSelectionModel *selection = m_listView->selectionModel(); 0154 for (int i = 0; i < count; ++i) { 0155 QStandardItem *item = rootItem->child(i); 0156 if (!selection->isSelected(m_model->indexFromItem(item))) { 0157 deselected = true; 0158 break; 0159 } 0160 } 0161 KMouthApp *theApp = qobject_cast<KMouthApp*>(parentWidget()); 0162 theApp->enableMenuEntries(selected, deselected); 0163 } 0164 0165 void PhraseList::configureCompletion() 0166 { 0167 completion->configure(); 0168 } 0169 0170 void PhraseList::configureCompletionCombo(const QStringList &list) 0171 { 0172 QString current = completion->currentWordList(); 0173 dictionaryCombo->clear(); 0174 if (list.isEmpty()) 0175 dictionaryCombo->hide(); 0176 else if (list.count() == 1) { 0177 dictionaryCombo->addItems(list); 0178 dictionaryCombo->setCurrentIndex(0); 0179 dictionaryCombo->hide(); 0180 } else { 0181 dictionaryCombo->addItems(list); 0182 dictionaryCombo->show(); 0183 0184 QStringList::ConstIterator it; 0185 int i = 0; 0186 for (it = list.begin(), i = 0; it != list.end(); ++it, ++i) { 0187 if (current == *it) { 0188 dictionaryCombo->setCurrentIndex(i); 0189 return; 0190 } 0191 } 0192 } 0193 } 0194 0195 void PhraseList::saveCompletionOptions() 0196 { 0197 KConfigGroup cg(KSharedConfig::openConfig(), "General Options"); 0198 cg.writeEntry("Show speak button", speakButton->isVisible() || !lineEdit->isVisible()); 0199 0200 KConfigGroup cg2(KSharedConfig::openConfig(), "Completion"); 0201 cg2.writeEntry("Mode", static_cast<int>(lineEdit->completionMode())); 0202 cg2.writeEntry("List", completion->currentWordList()); 0203 } 0204 0205 void PhraseList::readCompletionOptions() 0206 { 0207 KConfigGroup cg(KSharedConfig::openConfig(), "General Options"); 0208 if (!cg.readEntry("Show speak button", true)) 0209 speakButton->hide(); 0210 0211 if (KSharedConfig::openConfig()->hasGroup("Completion")) { 0212 KConfigGroup cg2(KSharedConfig::openConfig(), "Completion"); 0213 //int mode = cg2.readEntry("Mode", int(KGlobalSettings::completionMode())); 0214 //lineEdit->setCompletionMode(static_cast<KGlobalSettings::Completion>(mode)); 0215 0216 QString current = cg2.readEntry("List", QString()); 0217 const QStringList list = completion->wordLists(); 0218 QStringList::ConstIterator it; 0219 int i = 0; 0220 for (it = list.constBegin(), i = 0; it != list.constEnd(); ++it, ++i) { 0221 if (current == *it) { 0222 dictionaryCombo->setCurrentIndex(i); 0223 return; 0224 } 0225 } 0226 } 0227 } 0228 0229 void PhraseList::saveWordCompletion() 0230 { 0231 completion->save(); 0232 } 0233 0234 0235 void PhraseList::selectAllEntries() 0236 { 0237 m_listView->selectAll(); 0238 } 0239 0240 void PhraseList::deselectAllEntries() 0241 { 0242 m_listView->clearSelection(); 0243 } 0244 0245 void PhraseList::speak() 0246 { 0247 QString phrase = lineEdit->text(); 0248 if (phrase.isNull() || phrase.isEmpty()) 0249 speakListSelection(); 0250 else { 0251 insertIntoPhraseList(phrase, true); 0252 speakPhrase(phrase); 0253 } 0254 } 0255 0256 void PhraseList::cut() 0257 { 0258 if (lineEdit->hasSelectedText()) 0259 lineEdit->cut(); 0260 else 0261 cutListSelection(); 0262 } 0263 0264 void PhraseList::copy() 0265 { 0266 if (lineEdit->hasSelectedText()) 0267 lineEdit->copy(); 0268 else 0269 copyListSelection(); 0270 } 0271 0272 void PhraseList::paste() 0273 { 0274 lineEdit->paste(); 0275 } 0276 0277 void PhraseList::insert(const QString &s) 0278 { 0279 setEditLineText(s); 0280 } 0281 0282 void PhraseList::speakListSelection() 0283 { 0284 speakPhrase(getListSelection().join(QLatin1String("\n"))); 0285 } 0286 0287 void PhraseList::removeListSelection() 0288 { 0289 if (m_listView->selectionModel()->hasSelection()) { 0290 QList<QModelIndex> selected = m_listView->selectionModel()->selectedRows(); 0291 std::sort(selected.begin(), selected.end()); 0292 // Iterate over the rows backwards so we don't modify the .row of any indexes in selected. 0293 for (int i = selected.size() - 1; i >= 0; --i) { 0294 QModelIndex index = selected.at(i); 0295 m_model->removeRows(index.row(), 1); 0296 } 0297 } 0298 enableMenuEntries(); 0299 } 0300 0301 void PhraseList::cutListSelection() 0302 { 0303 copyListSelection(); 0304 removeListSelection(); 0305 } 0306 0307 void PhraseList::copyListSelection() 0308 { 0309 QApplication::clipboard()->setText(getListSelection().join(QLatin1String("\n"))); 0310 } 0311 0312 void PhraseList::lineEntered(const QString &phrase) 0313 { 0314 if (phrase.isNull() || phrase.isEmpty()) 0315 speakListSelection(); 0316 else { 0317 insertIntoPhraseList(phrase, true); 0318 speakPhrase(phrase); 0319 } 0320 } 0321 0322 void PhraseList::speakPhrase(const QString &phrase) 0323 { 0324 QApplication::setOverrideCursor(Qt::WaitCursor); 0325 KMouthApp *theApp = qobject_cast<KMouthApp*>(parentWidget()); 0326 QString language = completion->languageOfWordList(completion->currentWordList()); 0327 theApp->getTTSSystem()->speak(phrase, language); 0328 QApplication::restoreOverrideCursor(); 0329 } 0330 0331 void PhraseList::insertIntoPhraseList(const QString &phrase, bool clearEditLine) 0332 { 0333 int lastLine = m_model->rowCount() - 1; 0334 if ((lastLine == -1) || (phrase != m_model->data(m_model->index(lastLine, 0)).toString())) { 0335 QStandardItem *item = new QStandardItem(phrase); 0336 m_model->appendRow(item); 0337 if (clearEditLine) 0338 completion->addSentence(phrase); 0339 } 0340 0341 if (clearEditLine) { 0342 lineEdit->selectAll(); 0343 line.clear(); 0344 } 0345 enableMenuEntries(); 0346 } 0347 0348 void PhraseList::contextMenuRequested(const QPoint &pos) 0349 { 0350 QString name; 0351 if (existListSelection()) 0352 name = QStringLiteral("phraselist_selection_popup"); 0353 else 0354 name = QStringLiteral("phraselist_popup"); 0355 0356 KMouthApp *theApp = qobject_cast<KMouthApp*>(parentWidget()); 0357 KXMLGUIFactory *factory = theApp->factory(); 0358 QMenu *popup = (QMenu *)factory->container(name, theApp); 0359 if (popup != nullptr) { 0360 popup->exec(pos, nullptr); 0361 } 0362 } 0363 0364 void PhraseList::textChanged(const QString &s) 0365 { 0366 if (!isInSlot) { 0367 isInSlot = true; 0368 line = s; 0369 m_listView->setCurrentIndex(m_model->index(m_model->rowCount() - 1, 0)); 0370 m_listView->clearSelection(); 0371 isInSlot = false; 0372 } 0373 } 0374 0375 void PhraseList::selectionChanged() 0376 { 0377 if (!isInSlot) { 0378 isInSlot = true; 0379 0380 QStringList sel = getListSelection(); 0381 0382 if (sel.empty()) 0383 setEditLineText(line); 0384 else if (sel.count() == 1) 0385 setEditLineText(sel.first()); 0386 else { 0387 setEditLineText(QLatin1String("")); 0388 } 0389 isInSlot = false; 0390 } 0391 enableMenuEntries(); 0392 } 0393 0394 void PhraseList::setEditLineText(const QString &s) 0395 { 0396 lineEdit->end(false); 0397 while (!(lineEdit->text().isNull() || lineEdit->text().isEmpty())) 0398 lineEdit->backspace(); 0399 lineEdit->insert(s); 0400 } 0401 0402 void PhraseList::keyPressEvent(QKeyEvent *e) 0403 { 0404 if (e->key() == Qt::Key_Up) { 0405 bool selected = m_listView->selectionModel()->hasSelection(); 0406 0407 if (!selected) { 0408 m_listView->setCurrentIndex(m_model->index(m_model->rowCount() - 1, 0)); 0409 //listBox->ensureCurrentVisible (); 0410 } else { 0411 int curr = m_listView->currentIndex().row(); 0412 0413 if (curr == -1) { 0414 isInSlot = true; 0415 m_listView->clearSelection(); 0416 isInSlot = false; 0417 curr = m_model->rowCount() - 1; 0418 m_listView->setCurrentIndex(m_model->index(curr, 0)); 0419 //listBox->ensureCurrentVisible (); 0420 } else if (curr != 0) { 0421 isInSlot = true; 0422 m_listView->clearSelection(); 0423 isInSlot = false; 0424 m_listView->setCurrentIndex(m_model->index(curr - 1, 0)); 0425 //listBox->ensureCurrentVisible (); 0426 } 0427 } 0428 0429 e->accept(); 0430 } else if (e->key() == Qt::Key_Down) { 0431 bool selected = m_listView->selectionModel()->hasSelection(); 0432 0433 if (selected) { 0434 int curr = m_listView->currentIndex().row(); 0435 0436 if (curr == (int)m_model->rowCount() - 1) { 0437 m_listView->clearSelection(); 0438 } else if (curr != -1) { 0439 isInSlot = true; 0440 m_listView->clearSelection(); 0441 isInSlot = false; 0442 m_listView->setCurrentIndex(m_model->index(curr + 1, 0)); 0443 //listBox->ensureCurrentVisible (); 0444 } 0445 } 0446 e->accept(); 0447 } else if (e->modifiers() & Qt::ControlModifier) { 0448 if (e->key() == Qt::Key_C) { 0449 copy(); 0450 e->accept(); 0451 } else if (e->key() == Qt::Key_X) { 0452 cut(); 0453 e->accept(); 0454 } 0455 } else 0456 e->ignore(); 0457 } 0458 0459 void PhraseList::save() 0460 { 0461 // We want to save a history of spoken sentences here. However, as 0462 // the class PhraseBook does already provide a method for saving 0463 // phrase books in both the phrase book format and plain text file 0464 // format we use that method here. 0465 0466 PhraseBook book; 0467 QStandardItem *rootItem = m_model->invisibleRootItem(); 0468 int count = m_model->rowCount(); 0469 for (int i = 0; i < count; ++i) { 0470 QStandardItem *item = rootItem->child(i); 0471 book += PhraseBookEntry(Phrase(item->text())); 0472 } 0473 0474 QUrl url; 0475 if (book.save(this, i18n("Save As"), url, false) == -1) 0476 KMessageBox::error(this, i18n("There was an error saving file\n%1", url.url())); 0477 } 0478 0479 void PhraseList::open() 0480 { 0481 QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open File as History"), QUrl(), 0482 i18n("All Files (*);;Phrase Books (*.phrasebook);;Plain Text Files (*.txt)")); 0483 0484 if (!url.isEmpty()) 0485 open(url); 0486 } 0487 0488 void PhraseList::open(const QUrl &url) 0489 { 0490 // We want to open a history of spoken sentences here. However, as 0491 // the class PhraseBook does already provide a method for opening 0492 // both phrase books and plain text files we use that method here. 0493 0494 PhraseBook book; 0495 if (book.open(url)) { 0496 // convert PhraseBookEntryList -> QStringList 0497 QStringList list = book.toStringList(); 0498 m_model->clear(); 0499 QStringList::iterator it; 0500 for (it = list.begin(); it != list.end(); ++it) 0501 insertIntoPhraseList(*it, false); 0502 } else 0503 KMessageBox::error(this, i18n("There was an error loading file\n%1", url.toDisplayString())); 0504 } 0505