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