1/***************************************************************************
2 phraselist.cpp - description
3 -------------------
4 begin : Mit Sep 11 2002
5 copyright : (C) 2002 by Gunnar Schmi Dt
6 email : kmouth@schmi-dt.de
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18// application specific includes
19#include "phraselist.h"
20#include "phraselistitem.h"
21#include "phraseedit.h"
22#include "kmouth.h"
23#include "texttospeechsystem.h"
24#include "phrasebook/phrasebook.h"
25#include "wordcompletion/wordcompletion.h"
26
27// include files for Qt
28#include <QtGui/QPainter>
29#include <QtGui/QLayout>
30#include <QtGui/QApplication>
31#include <QtGui/QMenu>
32#include <QtGui/QClipboard>
33#include <QtGui/QVBoxLayout>
34#include <QtGui/QHBoxLayout>
35#include <QtGui/QKeyEvent>
36
37// include files for KDE
38#include <klineedit.h>
39#include <kcursor.h>
40#include <kiconloader.h>
41#include <klocale.h>
42#include <kfiledialog.h>
43#include <kcombobox.h>
44#include <kmessagebox.h>
45#include <kxmlguifactory.h>
46
47#include <stdlib.h>
48
49
50PhraseList::PhraseList(QWidget *parent, const char *name) : QWidget(parent) {
51 Q_UNUSED(name);
52 isInSlot = false;
53 // FIXME: Remove or change PaletteBase to Qt::OpaqueMode?
54 // setBackgroundMode(PaletteBase);
55 QVBoxLayout *layout = new QVBoxLayout (this);
56
57 listBox = new K3ListBox (this);
58 listBox->setFocusPolicy(Qt::NoFocus);
59 listBox->setSelectionMode (Q3ListBox::Extended);
60 listBox->setWhatsThis( i18n("This list contains the history of spoken sentences. You can select sentences and press the speak button for re-speaking."));
61 layout->addWidget(listBox);
62
63 QHBoxLayout *rowLayout = new QHBoxLayout ();
64 layout->addLayout(rowLayout);
65
66 completion = new WordCompletion();
67
68 dictionaryCombo = new KComboBox (this);
69 configureCompletionCombo(completion->wordLists());
70 rowLayout->addWidget(dictionaryCombo);
71
72 lineEdit = new PhraseEdit (QLatin1String( "" ), this);
73 lineEdit->setFocusPolicy(Qt::StrongFocus);
74 lineEdit->setFrame(true);
75 lineEdit->setEchoMode(QLineEdit::Normal);
76 lineEdit->setCompletionObject (completion);
77 lineEdit->setAutoDeleteCompletionObject(true);
78 lineEdit->setWhatsThis( i18n("Into this edit field you can type a phrase. Click on the speak button in order to speak the entered phrase."));
79 rowLayout->addWidget(lineEdit);
80 lineEdit->setFocus();
81
82 QIcon icon = KIconLoader::global()->loadIconSet(QLatin1String( "speak" ), KIconLoader::Small);
83 speakButton = new QPushButton (icon, i18n("&Speak"), this);
84 speakButton->setFocusPolicy(Qt::NoFocus);
85 speakButton->setAutoDefault(false);
86 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."));
87 rowLayout->addWidget(speakButton);
88
89 connect(dictionaryCombo, SIGNAL (activated(QString)), completion, SLOT (setWordList(QString)));
90 connect(completion, SIGNAL (wordListsChanged(QStringList)), this, SLOT (configureCompletionCombo(QStringList)));
91 connect(listBox, SIGNAL(selectionChanged()), SLOT(selectionChanged()));
92 connect(listBox, SIGNAL(contextMenuRequested(Q3ListBoxItem*,QPoint)), SLOT(contextMenuRequested(Q3ListBoxItem*,QPoint)));
93 connect(lineEdit, SIGNAL(returnPressed(QString)), SLOT(lineEntered(QString)));
94 connect(lineEdit, SIGNAL(textChanged(QString)), SLOT(textChanged(QString)));
95 connect(speakButton, SIGNAL(clicked()), SLOT(speak()));
96}
97
98PhraseList::~PhraseList() {
99 delete speakButton;
100 delete listBox;
101 delete lineEdit;
102}
103
104void PhraseList::print(QPrinter *pPrinter) {
105 PhraseBook book;
106 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = item->next()) {
107 book += PhraseBookEntry(Phrase(item->text()));
108 }
109
110 book.print (pPrinter);
111}
112
113QStringList PhraseList::getListSelection() {
114 QStringList res = QStringList();
115
116 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = item->next()) {
117 if (item->isSelected())
118 res += item->text();
119 }
120
121 return res;
122}
123
124bool PhraseList::existListSelection() {
125 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = item->next()) {
126 if (item->isSelected())
127 return true;
128 }
129
130 return false;
131}
132
133bool PhraseList::existEditSelection() {
134 return lineEdit->hasSelectedText();
135}
136
137void PhraseList::enableMenuEntries() {
138 bool deselected = false;
139 bool selected = false;
140 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = item->next()) {
141 if (item->isSelected())
142 selected = true;
143 else
144 deselected = true;
145 }
146 KMouthApp *theApp=(KMouthApp *) parentWidget();
147 theApp->enableMenuEntries (selected, deselected);
148}
149
150void PhraseList::configureCompletion() {
151 completion->configure();
152}
153
154void PhraseList::configureCompletionCombo(const QStringList &list) {
155 QString current = completion->currentWordList();
156 dictionaryCombo->clear();
157 if (list.isEmpty())
158 dictionaryCombo->hide();
159 else if (list.count() == 1) {
160 dictionaryCombo->addItems (list);
161 dictionaryCombo->setCurrentIndex (0);
162 dictionaryCombo->hide();
163 }
164 else {
165 dictionaryCombo->addItems (list);
166 dictionaryCombo->show();
167
168 QStringList::ConstIterator it;
169 int i = 0;
170 for (it = list.begin(), i = 0; it != list.end(); ++it, ++i) {
171 if (current == *it) {
172 dictionaryCombo->setCurrentIndex (i);
173 return;
174 }
175 }
176 }
177}
178
179void PhraseList::saveCompletionOptions(KConfig *config) {
180 KConfigGroup cg(config,"General Options");
181 cg.writeEntry("Show speak button", speakButton->isVisible() || !lineEdit->isVisible());
182
183 KConfigGroup cg2(config,"Completion");
184 cg2.writeEntry("Mode", static_cast<int>(lineEdit->completionMode()));
185 cg2.writeEntry("List", completion->currentWordList());
186}
187
188void PhraseList::readCompletionOptions(KConfig *config) {
189 KConfigGroup cg(config,"General Options");
190 if (!cg.readEntry("Show speak button", true))
191 speakButton->hide();
192
193 if (config->hasGroup ("Completion")) {
194 KConfigGroup cg2(config, "Completion");
195 int mode = cg2.readEntry ("Mode", int(KGlobalSettings::completionMode()));
196 lineEdit->setCompletionMode (static_cast<KGlobalSettings::Completion>(mode));
197
198 QString current = cg2.readEntry ("List", QString());
199 const QStringList list = completion->wordLists();
200 QStringList::ConstIterator it;
201 int i = 0;
202 for (it = list.constBegin(), i = 0; it != list.constEnd(); ++it, ++i) {
203 if (current == *it) {
204 dictionaryCombo->setCurrentIndex (i);
205 return;
206 }
207 }
208 }
209}
210
211void PhraseList::saveWordCompletion () {
212 completion->save();
213}
214
215
216void PhraseList::selectAllEntries () {
217 listBox->selectAll (true);
218}
219
220void PhraseList::deselectAllEntries () {
221 listBox->selectAll (false);
222}
223
224void PhraseList::speak () {
225 QString phrase = lineEdit->text();
226 if (phrase.isNull() || phrase.isEmpty())
227 speakListSelection();
228 else {
229 insertIntoPhraseList (phrase, true);
230 speakPhrase (phrase);
231 }
232}
233
234void PhraseList::cut() {
235 if (lineEdit->hasSelectedText())
236 lineEdit->cut();
237 else
238 cutListSelection();
239}
240
241void PhraseList::copy() {
242 if (lineEdit->hasSelectedText())
243 lineEdit->copy();
244 else
245 copyListSelection();
246}
247
248void PhraseList::paste() {
249 lineEdit->paste();
250}
251
252void PhraseList::insert (const QString &s) {
253 setEditLineText(s);
254}
255
256void PhraseList::speakListSelection () {
257 speakPhrase(getListSelection().join (QLatin1String( "\n" )));
258}
259
260void PhraseList::removeListSelection () {
261 Q3ListBoxItem *next;
262 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = next) {
263 next = item->next();
264
265 if (item->isSelected()) {
266 listBox->removeItem(listBox->index(item));
267 }
268 }
269 enableMenuEntries ();
270}
271
272void PhraseList::cutListSelection () {
273 copyListSelection ();
274 removeListSelection ();
275}
276
277void PhraseList::copyListSelection () {
278 QApplication::clipboard()->setText (getListSelection().join (QLatin1String( "\n" )));
279}
280
281void PhraseList::lineEntered (const QString &phrase) {
282 if (phrase.isNull() || phrase.isEmpty())
283 speakListSelection();
284 else {
285 insertIntoPhraseList (phrase, true);
286 speakPhrase (phrase);
287 }
288}
289
290void PhraseList::speakPhrase (const QString &phrase) {
291 // QApplication::setOverrideCursor (KCursor::WaitCursor, false);
292 QApplication::setOverrideCursor (Qt::WaitCursor);
293 KMouthApp *theApp=(KMouthApp *) parentWidget();
294 QString language = completion->languageOfWordList (completion->currentWordList());
295 theApp->getTTSSystem()->speak(phrase, language);
296 QApplication::restoreOverrideCursor ();
297}
298
299void PhraseList::insertIntoPhraseList (const QString &phrase, bool clearEditLine) {
300 int lastLine = listBox->count() - 1;
301 if ((lastLine == -1) || (phrase != listBox->text(lastLine))) {
302 listBox->insertItem(new PhraseListItem(phrase));
303 if (clearEditLine)
304 completion->addSentence (phrase);
305 }
306
307 if (clearEditLine) {
308 lineEdit->selectAll();
309 line.clear();
310 }
311 enableMenuEntries ();
312}
313
314void PhraseList::contextMenuRequested (Q3ListBoxItem *, const QPoint &pos) {
315 QString name;
316 if (existListSelection())
317 name = QLatin1String( "phraselist_selection_popup" );
318 else
319 name = QLatin1String( "phraselist_popup" );
320
321 KMouthApp *theApp=(KMouthApp *) parentWidget();
322 KXMLGUIFactory *factory = theApp->factory();
323 QMenu *popup = (QMenu *)factory->container(name,theApp);
324 if (popup != 0) {
325 popup->exec(pos, 0);
326 }
327}
328
329void PhraseList::textChanged (const QString &s) {
330 if (!isInSlot) {
331 isInSlot = true;
332 line = s;
333 listBox->setCurrentItem (listBox->count() - 1);
334 listBox->clearSelection ();
335 isInSlot = false;
336 }
337}
338
339void PhraseList::selectionChanged () {
340 if (!isInSlot) {
341 isInSlot = true;
342
343 QStringList sel = getListSelection();
344
345 if (sel.empty())
346 setEditLineText(line);
347 else if (sel.count() == 1)
348 setEditLineText(sel.first());
349 else {
350 setEditLineText(QLatin1String( "" ));
351 }
352 isInSlot = false;
353 }
354 enableMenuEntries ();
355}
356
357void PhraseList::setEditLineText(const QString &s) {
358 lineEdit->end(false);
359 while (!(lineEdit->text().isNull() || lineEdit->text().isEmpty()))
360 lineEdit->backspace();
361 lineEdit->insert(s);
362}
363
364void PhraseList::keyPressEvent (QKeyEvent *e) {
365 if (e->key() == Qt::Key_Up) {
366 bool selected = false;
367 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = item->next()) {
368 if (item->isSelected()) {
369 selected = true;
370 }
371 }
372
373 if (!selected) {
374 listBox->setCurrentItem (listBox->count() - 1);
375 listBox->setSelected (listBox->count() - 1, true);
376 listBox->ensureCurrentVisible ();
377 }
378 else {
379 int curr = listBox->currentItem();
380
381 if (curr == -1) {
382 isInSlot = true;
383 listBox->clearSelection();
384 isInSlot = false;
385 curr = listBox->count() - 1;
386 listBox->setCurrentItem (curr);
387 listBox->setSelected (curr, true);
388 listBox->ensureCurrentVisible ();
389 }
390 else if (curr != 0) {
391 isInSlot = true;
392 listBox->clearSelection();
393 isInSlot = false;
394 listBox->setCurrentItem (curr - 1);
395 listBox->setSelected (curr - 1, true);
396 listBox->ensureCurrentVisible ();
397 }
398 }
399
400 e->accept();
401 }
402 else if (e->key() == Qt::Key_Down) {
403 bool selected = false;
404 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = item->next()) {
405 if (item->isSelected()) {
406 selected = true;
407 }
408 }
409
410 if (selected) {
411 int curr = listBox->currentItem();
412
413 if (curr == (int)listBox->count() - 1) {
414 listBox->clearSelection();
415 }
416 else if (curr != -1) {
417 isInSlot = true;
418 listBox->clearSelection();
419 isInSlot = false;
420 listBox->setCurrentItem (curr + 1);
421 listBox->setSelected (curr + 1, true);
422 listBox->ensureCurrentVisible ();
423 }
424 }
425 e->accept();
426 }
427 else if (e->modifiers() & Qt::ControlModifier) {
428 if (e->key() == Qt::Key_C) {
429 copy();
430 e->accept();
431 }
432 else if (e->key() == Qt::Key_X) {
433 cut();
434 e->accept();
435 }
436 }
437 else
438 e->ignore();
439}
440
441void PhraseList::save () {
442 // We want to save a history of spoken sentences here. However, as
443 // the class PhraseBook does already provide a method for saving
444 // phrase books in both the phrase book format and plain text file
445 // format we use that method here.
446
447 PhraseBook book;
448 for (Q3ListBoxItem *item = listBox->firstItem(); item != 0; item = item->next()) {
449 book += PhraseBookEntry(Phrase(item->text()));
450 }
451
452 KUrl url;
453 if (book.save (this, i18n("Save As"), url, false) == -1)
454 KMessageBox::sorry(this,i18n("There was an error saving file\n%1", url.url() ));
455}
456
457void PhraseList::open () {
458 KUrl url=KFileDialog::getOpenUrl(KUrl(),
459 i18n("*|All Files\n*.phrasebook|Phrase Books (*.phrasebook)\n*.txt|Plain Text Files (*.txt)"), this, i18n("Open File as History"));
460
461 if(!url.isEmpty())
462 open (url);
463}
464
465void PhraseList::open (KUrl url) {
466 // We want to open a history of spoken sentences here. However, as
467 // the class PhraseBook does already provide a method for opening
468 // both phrase books and plain text files we use that method here.
469
470 PhraseBook book;
471 if (book.open (url)) {
472 // convert PhraseBookEntryList -> QStringList
473 QStringList list = book.toStringList();
474 listBox->clear();
475 QStringList::iterator it;
476 for (it = list.begin(); it != list.end(); ++it)
477 insertIntoPhraseList (*it, false);
478 }
479 else
480 KMessageBox::sorry(this,i18n("There was an error loading file\n%1", url.url() ));
481}
482
483#include "phraselist.moc"
484