1 /***************************************************************************
2  *   Copyright (C) 2002 by Gunnar Schmi Dt <kmouth@schmi-dt.de             *
3  *             (C) 2015 by Jeremy Whiting <jpwhiting@kde.org>              *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
19  ***************************************************************************/
20 
21 // application specific includes
22 #include "kmouth.h"
23 
24 // include files for Qt
25 #include <QMenu>
26 #include <QMenuBar>
27 #include <QPrintDialog>
28 #include <QStandardPaths>
29 #include <QStatusBar>
30 
31 // include files for KDE
32 #include <KActionCollection>
33 #include <KConfigGroup>
34 #include <KLocalizedString>
35 #include <KStandardAction>
36 #include <KStandardShortcut>
37 #include <KToggleAction>
38 #include <KToolBar>
39 #include <KXMLGUIFactory>
40 
41 #include "phraselist.h"
42 #include "phrasebook/phrasebook.h"
43 #include "phrasebook/phrasebookdialog.h"
44 #include "optionsdialog.h"
45 #include "configwizard.h"
46 
KMouthApp(QWidget *,const QString & name)47 KMouthApp::KMouthApp(QWidget* , const QString& name): KXmlGuiWindow(nullptr)
48 {
49     setWindowIcon(QIcon::fromTheme(QStringLiteral("kmouth")));
50     setObjectName(name);
51     isConfigured = false;
52 
53     ///////////////////////////////////////////////////////////////////
54     // call inits to invoke all other construction parts
55     initStatusBar();
56     initPhraseList();
57     initActions();
58     optionsDialog = new OptionsDialog(this);
59     connect(optionsDialog, &OptionsDialog::configurationChanged,
60             this, &KMouthApp::slotConfigurationChanged);
61     connect(optionsDialog, &OptionsDialog::configurationChanged,
62             phraseList, &PhraseList::configureCompletion);
63 
64     phrases = new KActionCollection(static_cast<QWidget*>(this));
65 
66     readOptions();
67     ConfigWizard *wizard = new ConfigWizard(this);
68     if (wizard->configurationNeeded()) {
69         if (wizard->requestConfiguration()) {
70             isConfigured = true;
71             saveOptions();
72             wizard->saveConfig();
73             readOptions();
74         } else
75             isConfigured = false;
76     } else
77         isConfigured = true;
78     delete wizard;
79 
80     if (isConfigured) {
81         phraseList->configureCompletion();
82     }
83 
84     ///////////////////////////////////////////////////////////////////
85     // disable actions at startup
86     fileSaveAs->setEnabled(false);
87     filePrint->setEnabled(false);
88 
89     printer = nullptr;
90 }
91 
~KMouthApp()92 KMouthApp::~KMouthApp()
93 {
94     delete printer;
95 }
96 
configured()97 bool KMouthApp::configured()
98 {
99     return isConfigured;
100 }
101 
initActions()102 void KMouthApp::initActions()
103 {
104 // The "File" menu
105     fileOpen = actionCollection()->addAction(QStringLiteral("file_open"));
106     fileOpen->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
107     fileOpen->setText(i18n("&Open as History..."));
108     actionCollection()->setDefaultShortcuts(fileOpen, KStandardShortcut::open());
109     connect(fileOpen, &QAction::triggered, this, &KMouthApp::slotFileOpen);
110     fileOpen->setToolTip(i18n("Opens an existing file as history"));
111     fileOpen->setWhatsThis(i18n("Opens an existing file as history"));
112 
113     fileSaveAs = actionCollection()->addAction(QStringLiteral("file_save_as"));
114     fileSaveAs->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
115     fileSaveAs->setText(i18n("Save &History As..."));
116     actionCollection()->setDefaultShortcuts(fileSaveAs, KStandardShortcut::save());
117     connect(fileSaveAs, &QAction::triggered, this, &KMouthApp::slotFileSaveAs);
118     fileSaveAs->setToolTip(i18n("Saves the actual history as..."));
119     fileSaveAs->setWhatsThis(i18n("Saves the actual history as..."));
120 
121     filePrint = actionCollection()->addAction(QStringLiteral("file_print"));
122     filePrint->setIcon(QIcon::fromTheme(QStringLiteral("document-print")));
123     filePrint->setText(i18n("&Print History..."));
124     actionCollection()->setDefaultShortcuts(filePrint, KStandardShortcut::print());
125     connect(filePrint, &QAction::triggered, this, &KMouthApp::slotFilePrint);
126     filePrint->setToolTip(i18n("Prints out the actual history"));
127     filePrint->setWhatsThis(i18n("Prints out the actual history"));
128 
129     fileQuit = KStandardAction::quit(this, SLOT(slotFileQuit()), actionCollection());
130     fileQuit->setToolTip(i18n("Quits the application"));
131     fileQuit->setWhatsThis(i18n("Quits the application"));
132 
133 // The "Edit" menu
134     editCut = KStandardAction::cut(phraseList, SLOT(cut()), actionCollection());
135     editCut->setToolTip(i18n("Cuts the selected section and puts it to the clipboard"));
136     editCut->setWhatsThis(i18n("Cuts the selected section and puts it to the clipboard. If there is some text selected in the edit field it is placed it on the clipboard. Otherwise the selected sentences in the history (if any) are placed on the clipboard."));
137 
138     editCopy = KStandardAction::copy(phraseList, SLOT(copy()), actionCollection());
139     editCopy->setToolTip(i18n("Copies the selected section to the clipboard"));
140     editCopy->setWhatsThis(i18n("Copies the selected section to the clipboard. If there is some text selected in the edit field it is copied to the clipboard. Otherwise the selected sentences in the history (if any) are copied to the clipboard."));
141 
142     editPaste = KStandardAction::paste(phraseList, SLOT(paste()), actionCollection());
143     editPaste->setToolTip(i18n("Pastes the clipboard contents to current position"));
144     editPaste->setWhatsThis(i18n("Pastes the clipboard contents at the current cursor position into the edit field."));
145 
146     editSpeak = actionCollection()->addAction(QStringLiteral("edit_speak"));
147     editSpeak->setIcon(QIcon::fromTheme(QStringLiteral("text-speak")));
148     editSpeak->setText(i18nc("Start speaking", "&Speak"));
149     connect(editSpeak, &QAction::triggered, phraseList, &PhraseList::speak);
150     editSpeak->setToolTip(i18n("Speaks the currently active sentence(s)"));
151     editSpeak->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."));
152 
153 // The "Phrase book" menu
154     phrasebookEdit = actionCollection()->addAction(QStringLiteral("phrasebook_edit"));
155     phrasebookEdit->setText(i18n("&Edit..."));
156     connect(phrasebookEdit, &QAction::triggered, this, &KMouthApp::slotEditPhrasebook);
157 
158 // The "Options" menu
159     viewMenuBar = KStandardAction::showMenubar(this, SLOT(slotViewMenuBar()), actionCollection());
160     // FIXME: Disable so it will compile.
161     // viewToolBar = KStandardAction::showToolbar(this, SLOT(slotViewToolBar()), actionCollection());
162     // viewToolBar->setToolTip(i18n("Enables/disables the toolbar"));
163     // viewToolBar->setWhatsThis (i18n("Enables/disables the toolbar"));
164 
165     viewPhrasebookBar = actionCollection()->add<KToggleAction>(QStringLiteral("showPhrasebookBar"));
166     viewPhrasebookBar->setText(i18n("Show P&hrasebook Bar"));
167     connect(viewPhrasebookBar, &QAction::triggered, this, &KMouthApp::slotViewPhrasebookBar);
168     viewPhrasebookBar->setToolTip(i18n("Enables/disables the phrasebook bar"));
169     viewPhrasebookBar->setWhatsThis(i18n("Enables/disables the phrasebook bar"));
170 
171     viewStatusBar = KStandardAction::showStatusbar(this, SLOT(slotViewStatusBar()), actionCollection());
172     viewStatusBar->setToolTip(i18n("Enables/disables the statusbar"));
173     viewStatusBar->setWhatsThis(i18n("Enables/disables the statusbar"));
174 
175     configureTTS = actionCollection()->addAction(QStringLiteral("configureTTS"));
176     configureTTS->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
177     configureTTS->setText(i18n("&Configure KMouth..."));
178     connect(configureTTS, &QAction::triggered, this, &KMouthApp::slotConfigureTTS);
179     configureTTS->setToolTip(i18n("Opens the configuration dialog"));
180     configureTTS->setWhatsThis(i18n("Opens the configuration dialog"));
181 
182 // The "Help" menu
183     // The "Help" menu will automatically get created.
184 
185 // The popup menu of the list of spoken sentences
186     phraseListSpeak = actionCollection()->addAction(QStringLiteral("phraselist_speak"));
187     phraseListSpeak->setIcon(QIcon::fromTheme(QStringLiteral("text-speak")));
188     phraseListSpeak->setText(i18n("&Speak"));
189     phraseListSpeak->setToolTip(i18n("Speaks the currently selected phrases in the history"));
190     connect(phraseListSpeak, &QAction::triggered, phraseList, &PhraseList::speakListSelection);
191     phraseListSpeak->setWhatsThis(i18n("Speaks the currently selected phrases in the history"));
192 
193     phraseListRemove = actionCollection()->addAction(QStringLiteral("phraselist_remove"));
194     phraseListRemove->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
195     phraseListRemove->setText(i18n("&Delete"));
196     connect(phraseListRemove, &QAction::triggered, phraseList, &PhraseList::removeListSelection);
197     phraseListRemove->setToolTip(i18n("Deletes the currently selected phrases from the history"));
198     phraseListRemove->setWhatsThis(i18n("Deletes the currently selected phrases from the history"));
199 
200     phraseListCut = actionCollection()->addAction(QStringLiteral("phraselist_cut"));
201     phraseListCut->setIcon(QIcon::fromTheme(QStringLiteral("edit-cut")));
202     phraseListCut->setText(i18n("Cu&t"));
203     connect(phraseListCut, &QAction::triggered, phraseList, &PhraseList::cutListSelection);
204     phraseListCut->setToolTip(i18n("Cuts the currently selected phrases from the history and puts them to the clipboard"));
205     phraseListCut->setWhatsThis(i18n("Cuts the currently selected phrases from the history and puts them to the clipboard"));
206 
207     phraseListCopy = actionCollection()->addAction(QStringLiteral("phraselist_copy"));
208     phraseListCopy->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
209     phraseListCopy->setText(i18n("&Copy"));
210     connect(phraseListCopy, &QAction::triggered, phraseList, &PhraseList::copyListSelection);
211     phraseListCut->setToolTip(i18n("Copies the currently selected phrases from the history to the clipboard"));
212     phraseListCut->setWhatsThis(i18n("Copies the currently selected phrases from the history to the clipboard"));
213 
214     phraselistSelectAll = actionCollection()->addAction(QStringLiteral("phraselist_select_all"));
215     phraselistSelectAll->setText(i18n("Select &All Entries"));
216     connect(phraselistSelectAll, &QAction::triggered, phraseList, &PhraseList::selectAllEntries);
217     phraselistSelectAll->setToolTip(i18n("Selects all phrases in the history"));
218     phraselistSelectAll->setWhatsThis(i18n("Selects all phrases in the history"));
219 
220     phraselistDeselectAll = actionCollection()->addAction(QStringLiteral("phraselist_deselect_all"));
221     phraselistDeselectAll->setText(i18n("D&eselect All Entries"));
222     connect(phraselistDeselectAll, &QAction::triggered, phraseList, &PhraseList::deselectAllEntries);
223     phraselistDeselectAll->setToolTip(i18n("Deselects all phrases in the history"));
224     phraselistDeselectAll->setWhatsThis(i18n("Deselects all phrases in the history"));
225 
226 // The popup menu of the edit field
227     // The popup menu of the edit field will automatically get created.
228 
229     // use the absolute path to your kmouthui.rc file for testing purpose in createGUI();
230     createGUI();
231 }
232 
initStatusBar()233 void KMouthApp::initStatusBar()
234 {
235     ///////////////////////////////////////////////////////////////////
236     // STATUSBAR
237     // TODO: add your own items you need for displaying current application status.
238     m_statusLabel = new QLabel(i18nc("The job is done", "Ready."));
239     statusBar()->addPermanentWidget(m_statusLabel);
240 }
241 
initPhraseList()242 void KMouthApp::initPhraseList()
243 {
244     ////////////////////////////////////////////////////////////////////
245     // create the main widget here that is managed by KTMainWindow's view-region and
246     // connect the widget to your document to display document contents.
247 
248     phraseList = new PhraseList(this);
249     setCentralWidget(phraseList);
250 }
251 
openDocumentFile(const QUrl & url)252 void KMouthApp::openDocumentFile(const QUrl &url)
253 {
254     slotStatusMsg(i18n("Opening file..."));
255 
256     phraseList->open(url);
257     slotStatusMsg(i18nc("The job is done", "Ready."));
258 }
259 
saveOptions()260 void KMouthApp::saveOptions()
261 {
262     if (isConfigured) {
263         KConfigGroup cg(KSharedConfig::openConfig(), "General Options");
264         cg.writeEntry("Geometry", size());
265         cg.writeEntry("Show Menubar", viewMenuBar->isChecked());
266         // FIXME: Toolbar disabled so it will compile.
267         // cg.writeEntry("Show Toolbar", viewToolBar->isChecked());
268         cg.writeEntry("Show Phrasebook Bar", viewPhrasebookBar->isChecked());
269         cg.writeEntry("Show Statusbar", viewStatusBar->isChecked());
270         // FIXME: KToolBar no longer has barPos() method.
271         // cg.writeEntry("ToolBarPos", (int) toolBar("mainToolBar")->barPos());
272 
273         if (phraseList != nullptr)
274             phraseList->saveCompletionOptions();
275         optionsDialog->saveOptions();
276         KConfigGroup cg2(KSharedConfig::openConfig(), "mainToolBar");
277         toolBar(QStringLiteral("mainToolBar"))->saveSettings(cg2);
278         KConfigGroup cg3(KSharedConfig::openConfig(), "phrasebookBar");
279         toolBar(QStringLiteral("phrasebookBar"))->saveSettings(cg);
280     }
281 }
282 
283 
readOptions()284 void KMouthApp::readOptions()
285 {
286     KConfigGroup cg(KSharedConfig::openConfig(), "General Options");
287 
288     // bar status settings
289     bool bViewMenubar = cg.readEntry("Show Menubar", true);
290     viewMenuBar->setChecked(bViewMenubar);
291     slotViewMenuBar();
292 
293     // FIXME: Toolbar disabled so it will compile.
294     // bool bViewToolbar = cg.readEntry("Show Toolbar", QVariant(true)).toBool();
295     // viewToolBar->setChecked(bViewToolbar);
296     // slotViewToolBar();
297 
298     bool bViewPhrasebookbar = cg.readEntry("Show Phrasebook Bar", true);
299     viewPhrasebookBar->setChecked(bViewPhrasebookbar);
300 
301     bool bViewStatusbar = cg.readEntry("Show Statusbar", true);
302     viewStatusBar->setChecked(bViewStatusbar);
303     slotViewStatusBar();
304 
305     // bar position settings
306     // FIXME:
307     // KToolBar::BarPosition toolBarPos;
308     // toolBarPos=(KToolBar::BarPosition) cg.readEntry("ToolBarPos", int(KToolBar::Top));
309     // toolBar("mainToolBar")->setBarPos(toolBarPos);
310 
311     QSize size = cg.readEntry("Geometry", QSize());
312     if (!size.isEmpty()) {
313         resize(size);
314     }
315 
316     optionsDialog->readOptions();
317 
318     toolBar(QStringLiteral("mainToolBar"))->applySettings(KSharedConfig::openConfig()->group("mainToolBar"));
319     toolBar(QStringLiteral("phrasebookBar"))->applySettings(KSharedConfig::openConfig()->group("phrasebookBar"));
320 
321     slotPhrasebookConfirmed();
322     if (phraseList != nullptr)
323         phraseList->readCompletionOptions();
324 }
325 
queryClose()326 bool KMouthApp::queryClose()
327 {
328     saveOptions();
329     return true;
330 }
331 
enableMenuEntries(bool existSelectedEntries,bool existDeselectedEntries)332 void KMouthApp::enableMenuEntries(bool existSelectedEntries, bool existDeselectedEntries)
333 {
334     bool existEntries = existSelectedEntries | existDeselectedEntries;
335     fileSaveAs->setEnabled(existEntries);
336     filePrint->setEnabled(existEntries);
337 
338     phraselistSelectAll->setEnabled(existDeselectedEntries);
339 
340     phraselistDeselectAll->setEnabled(existSelectedEntries);
341     phraseListSpeak->setEnabled(existSelectedEntries);
342     phraseListRemove->setEnabled(existSelectedEntries);
343     phraseListCut->setEnabled(existSelectedEntries);
344     phraseListCopy->setEnabled(existSelectedEntries);
345 }
346 
347 /////////////////////////////////////////////////////////////////////
348 // SLOT IMPLEMENTATION
349 /////////////////////////////////////////////////////////////////////
350 
slotFileOpen()351 void KMouthApp::slotFileOpen()
352 {
353     slotStatusMsg(i18n("Opening file..."));
354 
355     phraseList->open();
356 
357     slotStatusMsg(i18nc("The job is done", "Ready."));
358 }
359 
slotFileSaveAs()360 void KMouthApp::slotFileSaveAs()
361 {
362     slotStatusMsg(i18n("Saving history with a new filename..."));
363 
364     phraseList->save();
365 
366     slotStatusMsg(i18nc("The job is done", "Ready."));
367 }
368 
slotFilePrint()369 void KMouthApp::slotFilePrint()
370 {
371     slotStatusMsg(i18n("Printing..."));
372 
373     if (printer == nullptr) {
374         printer = new QPrinter();
375     }
376 
377     QPrintDialog *printDialog = new QPrintDialog(printer, nullptr);
378 
379     if (printDialog->exec()) {
380         phraseList->print(printer);
381     }
382 
383     slotStatusMsg(i18nc("The job is done", "Ready."));
384 }
385 
slotFileQuit()386 void KMouthApp::slotFileQuit()
387 {
388     slotStatusMsg(i18nc("Shutting down the application", "Exiting..."));
389     saveOptions();
390     // close the first window, the list makes the next one the first again.
391     // This ensures that queryClose() is called on each window to ask for closing
392     KMainWindow* w;
393     if (!memberList().isEmpty()) {
394         for (int i = 0; i < memberList().size(); ++i) {
395             // only close the window if the closeEvent is accepted. If the user presses Cancel on the saveModified() dialog,
396             // the window and the application stay open.
397             w = memberList().at(i);
398             if (!w->close())
399                 break;
400 #ifdef __GNUC__
401 #warning "kde4: how remove it ?.???"
402 #endif
403             //memberList()->removeRef(w);
404         }
405     }
406 }
407 
slotEditPhrasebook()408 void KMouthApp::slotEditPhrasebook()
409 {
410     PhraseBookDialog *phraseBookDialog = PhraseBookDialog::get();
411     // As we do not know whether the we are already connected to the slot,
412     // we first disconnect and then connect again.
413     disconnect(phraseBookDialog, &PhraseBookDialog::phrasebookConfirmed,
414                this, &KMouthApp::slotPhrasebookConfirmed);
415     connect(phraseBookDialog, &PhraseBookDialog::phrasebookConfirmed,
416             this, &KMouthApp::slotPhrasebookConfirmed);
417 
418     // As we do not know whether the phrase book edit window is already open,
419     // we first open and then raise it, so that it is surely the top window.
420     phraseBookDialog->show();
421     phraseBookDialog->raise();
422 }
423 
slotViewMenuBar()424 void KMouthApp::slotViewMenuBar()
425 {
426     slotStatusMsg(i18n("Toggling menubar..."));
427 
428     if (!viewMenuBar->isChecked())
429         menuBar()->hide();
430     else
431         menuBar()->show();
432 
433     slotStatusMsg(i18nc("The job is done", "Ready."));
434 }
435 
slotViewToolBar()436 void KMouthApp::slotViewToolBar()
437 {
438     slotStatusMsg(i18n("Toggling toolbar..."));
439     ///////////////////////////////////////////////////////////////////
440     // turn Toolbar on or off
441     if (!viewToolBar->isChecked()) {
442         toolBar(QStringLiteral("mainToolBar"))->hide();
443     } else {
444         toolBar(QStringLiteral("mainToolBar"))->show();
445     }
446 
447     slotStatusMsg(i18nc("The job is done", "Ready."));
448 }
449 
slotViewPhrasebookBar()450 void KMouthApp::slotViewPhrasebookBar()
451 {
452     slotStatusMsg(i18n("Toggling phrasebook bar..."));
453     ///////////////////////////////////////////////////////////////////
454     // turn Toolbar on or off
455     if (!viewPhrasebookBar->isChecked()) {
456         toolBar(QStringLiteral("phrasebookBar"))->hide();
457     } else {
458         toolBar(QStringLiteral("phrasebookBar"))->show();
459     }
460 
461     slotStatusMsg(i18nc("The job is done", "Ready."));
462 }
463 
slotViewStatusBar()464 void KMouthApp::slotViewStatusBar()
465 {
466     slotStatusMsg(i18n("Toggle the statusbar..."));
467     ///////////////////////////////////////////////////////////////////
468     //turn Statusbar on or off
469     if (!viewStatusBar->isChecked()) {
470         statusBar()->hide();
471     } else {
472         statusBar()->show();
473     }
474 
475     slotStatusMsg(i18nc("The job is done", "Ready."));
476 }
477 
slotConfigureTTS()478 void KMouthApp::slotConfigureTTS()
479 {
480     phraseList->saveWordCompletion();
481     optionsDialog->show();
482 }
483 
484 
slotStatusMsg(const QString & text)485 void KMouthApp::slotStatusMsg(const QString &text)
486 {
487     ///////////////////////////////////////////////////////////////////
488     // change status message permanently
489     m_statusLabel->setText(text);
490 }
491 
slotPhrasebookConfirmed()492 void KMouthApp::slotPhrasebookConfirmed()
493 {
494     QString standardBook = QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("standard.phrasebook"));
495     if (!standardBook.isEmpty()) {
496         PhraseBook book;
497         book.open(QUrl::fromLocalFile(standardBook));
498         QString name = QStringLiteral("phrasebooks");
499         QMenu *popup = (QMenu *)factory()->container(name, this);
500         KToolBar *toolbar = toolBar(QStringLiteral("phrasebookBar"));
501 
502         delete phrases;
503         phrases = new KActionCollection(actionCollection());
504         book.addToGUI(popup, toolbar, phrases, this, SLOT(slotPhraseSelected(QString)));
505     }
506 }
507 
slotConfigurationChanged()508 void KMouthApp::slotConfigurationChanged()
509 {
510     optionsDialog->saveOptions();
511 }
512 
slotPhraseSelected(const QString & phrase)513 void KMouthApp::slotPhraseSelected(const QString &phrase)
514 {
515     phraseList->insert(phrase);
516     if (optionsDialog->isSpeakImmediately())
517         phraseList->speak();
518 }
519 
getTTSSystem() const520 TextToSpeechSystem *KMouthApp::getTTSSystem() const
521 {
522     return optionsDialog->getTTSSystem();
523 }
524 
525