1 /*********
2 *
3 * This file is part of BibleTime's source code, http://www.bibletime.info/.
4 *
5 * Copyright 1999-2016 by the BibleTime developers.
6 * The BibleTime source code is licensed under the GNU General Public License version 2.0.
7 *
8 **********/
9
10 #include "bibletime.h"
11
12 #include <cstdlib>
13 #include <exception>
14 #include <QAction>
15 #include <QApplication>
16 #include <QCloseEvent>
17 #include <QDebug>
18 #include <QInputDialog>
19 #include <QMdiSubWindow>
20 #include <QSplashScreen>
21 #include <QSplitter>
22 #include "backend/config/btconfig.h"
23 #include "backend/drivers/cswordbiblemoduleinfo.h"
24 #include "backend/drivers/cswordbookmoduleinfo.h"
25 #include "backend/drivers/cswordcommentarymoduleinfo.h"
26 #include "backend/drivers/cswordlexiconmoduleinfo.h"
27 #include "backend/drivers/cswordmoduleinfo.h"
28 #include "backend/keys/cswordldkey.h"
29 #include "backend/keys/cswordversekey.h"
30 #include "bibletimeapp.h"
31 #include "frontend/btaboutmoduledialog.h"
32 #include "frontend/cmdiarea.h"
33 #include "frontend/display/btfindwidget.h"
34 #include "frontend/displaywindow/btactioncollection.h"
35 #include "frontend/displaywindow/cbiblereadwindow.h"
36 #include "frontend/displaywindow/cbookreadwindow.h"
37 #include "frontend/displaywindow/ccommentaryreadwindow.h"
38 #include "frontend/displaywindow/cdisplaywindow.h"
39 #include "frontend/displaywindow/chtmlwritewindow.h"
40 #include "frontend/displaywindow/clexiconreadwindow.h"
41 #include "frontend/displaywindow/cplainwritewindow.h"
42 #include "frontend/displaywindow/creadwindow.h"
43 #include "frontend/keychooser/ckeychooser.h"
44 #include "frontend/messagedialog.h"
45 #include "frontend/searchdialog/csearchdialog.h"
46 #include "util/btassert.h"
47 #include "util/cresmgr.h"
48 #include "util/directory.h"
49
50
51 BibleTime *BibleTime::m_instance = nullptr;
52
BibleTime(QWidget * parent,Qt::WindowFlags flags)53 BibleTime::BibleTime(QWidget *parent, Qt::WindowFlags flags)
54 : QMainWindow(parent, flags)
55 {
56 namespace DU = util::directory;
57
58 BT_ASSERT(!m_instance);
59 m_instance = this;
60
61 QSplashScreen *splash = nullptr;
62 QString splashHtml;
63
64 if (btConfig().value<bool>("GUI/showSplashScreen", true)) {
65 splashHtml = "<div style='background:transparent;color:white;font-weight:bold'>%1"
66 "</div>";
67
68 static const char splash1[] = "startuplogo.png";
69 static const char splash2[] = "startuplogo_christmas.png";
70 static const char splash3[] = "startuplogo_easter.jpg";
71 static const char * const splashes[3] = {
72 splash1, splash2, splash3
73 };
74 QString splashImage = DU::getPicsDir().canonicalPath().append("/")
75 .append(splashes[rand() % 3]);
76 QPixmap pm;
77 if (!pm.load(splashImage)) {
78 qWarning("Can't load startuplogo! Check your installation.");
79 }
80 splash = new QSplashScreen(this, pm);
81 splash->setAttribute(Qt::WA_DeleteOnClose);
82 splash->finish(this);
83 splash->showMessage(splashHtml.arg(tr("Initializing the SWORD engine...")),
84 Qt::AlignCenter);
85 splash->show();
86 qApp->processEvents();
87 }
88 initBackends();
89
90 if (splash != nullptr) {
91 splash->showMessage(splashHtml.arg(tr("Creating BibleTime's user interface...")),
92 Qt::AlignCenter);
93 qApp->processEvents();
94 }
95 initView();
96
97 if (splash != nullptr) {
98 splash->showMessage(splashHtml.arg(tr("Initializing menu- and toolbars...")),
99 Qt::AlignCenter);
100 qApp->processEvents();
101 }
102 initActions();
103 initMenubar();
104 initToolbars();
105 initConnections();
106
107 setWindowTitle("BibleTime " BT_VERSION);
108 setWindowIcon(CResMgr::mainWindow::icon());
109 retranslateUi();
110 }
111
~BibleTime()112 BibleTime::~BibleTime() {
113 // delete m_dcopInterface;
114 // The backend is deleted by the BibleTimeApp instance
115 #ifndef NDEBUG
116 deleteDebugWindow();
117 #endif
118 saveProfile();
119 }
120
121 namespace {
122
createReadInstance(QList<CSwordModuleInfo * > const modules,CMDIArea * const parent)123 CReadWindow * createReadInstance(QList<CSwordModuleInfo *> const modules,
124 CMDIArea * const parent)
125 {
126 switch (modules.first()->type()) {
127 case CSwordModuleInfo::Bible:
128 return new CBibleReadWindow(modules, parent);
129 case CSwordModuleInfo::Commentary:
130 return new CCommentaryReadWindow(modules, parent);
131 case CSwordModuleInfo::Lexicon:
132 return new CLexiconReadWindow(modules, parent);
133 case CSwordModuleInfo::GenericBook:
134 return new CBookReadWindow(modules, parent);
135 default:
136 qFatal("unknown module type");
137 std::terminate();
138 }
139 }
140
141 } // anonymous namespace
142
143 /** Creates a new presenter in the MDI area according to the type of the module. */
createReadDisplayWindow(QList<CSwordModuleInfo * > modules,const QString & key)144 CDisplayWindow* BibleTime::createReadDisplayWindow(QList<CSwordModuleInfo*> modules, const QString& key) {
145 qApp->setOverrideCursor( QCursor(Qt::WaitCursor) );
146 // qDebug() << "BibleTime::createReadDisplayWindow(QList<CSwordModuleInfo*> modules, const QString& key)";
147 CDisplayWindow * const displayWindow = createReadInstance(modules, m_mdi);
148 if ( displayWindow ) {
149 displayWindow->init();
150 m_mdi->addSubWindow(displayWindow);
151 displayWindow->show();
152 // if (!key.isEmpty())
153 displayWindow->lookupKey(key);
154 }
155 // We have to process pending events here, otherwise displayWindow is not fully painted
156 qApp->processEvents();
157 // Now all events, including mouse clicks for the displayWindow have been handled
158 // and we can let the user click the same module again
159 //m_bookshelfPage->unfreezeModules(modules);
160 qApp->restoreOverrideCursor();
161 return displayWindow;
162 }
163
164
165 /** Creates a new presenter in the MDI area according to the type of the module. */
createReadDisplayWindow(CSwordModuleInfo * module,const QString & key)166 CDisplayWindow* BibleTime::createReadDisplayWindow(CSwordModuleInfo* module, const QString& key) {
167 return createReadDisplayWindow(QList<CSwordModuleInfo*>() << module, key);
168 }
169
createWriteDisplayWindow(CSwordModuleInfo * module,const QString & key,CPlainWriteWindow::WriteWindowType type)170 CDisplayWindow * BibleTime::createWriteDisplayWindow(CSwordModuleInfo * module, const QString & key, CPlainWriteWindow::WriteWindowType type) {
171 qApp->setOverrideCursor( QCursor(Qt::WaitCursor) );
172
173 CDisplayWindow * const displayWindow =
174 (type == CPlainWriteWindow::HTMLWindow)
175 ? new CHTMLWriteWindow(QList<CSwordModuleInfo *>() << module, m_mdi)
176 : new CPlainWriteWindow(QList<CSwordModuleInfo *>() << module, m_mdi);
177 displayWindow->init();
178 m_mdi->addSubWindow(displayWindow);
179 if (m_mdi->subWindowList().isEmpty())
180 displayWindow->showMaximized();
181 else
182 displayWindow->show();
183 displayWindow->lookupKey(key);
184
185 qApp->restoreOverrideCursor();
186 return displayWindow;
187 }
188
moduleEditPlain(CSwordModuleInfo * module)189 CDisplayWindow* BibleTime::moduleEditPlain(CSwordModuleInfo *module) {
190 /// \todo Refactor this.
191 return createWriteDisplayWindow(module,
192 QString::null,
193 CPlainWriteWindow::PlainTextWindow);
194 }
195
moduleEditHtml(CSwordModuleInfo * module)196 CDisplayWindow* BibleTime::moduleEditHtml(CSwordModuleInfo *module) {
197 /// \todo Refactor this.
198 return createWriteDisplayWindow(module,
199 QString::null,
200 CPlainWriteWindow::HTMLWindow);
201 }
202
203
searchInModule(CSwordModuleInfo * module)204 void BibleTime::searchInModule(CSwordModuleInfo *module) {
205 /// \todo Refactor this.
206 BtConstModuleList modules;
207 modules.append(module);
208 Search::CSearchDialog::openDialog(modules, QString::null);
209 }
210
moduleUnlock(CSwordModuleInfo * module,QWidget * parent)211 bool BibleTime::moduleUnlock(CSwordModuleInfo *module, QWidget *parent) {
212 /// \todo Write a proper unlocking dialog with integrated error messages.
213 QString unlockKey;
214 bool ok;
215 for (;;) {
216 unlockKey = QInputDialog::getText(
217 parent, tr("Unlock Work"), tr("Enter the unlock key for %1.").arg(module->name()),
218 QLineEdit::Normal, module->config(CSwordModuleInfo::CipherKey), &ok
219 );
220 if (!ok) return false;
221 module->unlock(unlockKey);
222
223 /// \todo refactor this module reload
224 /* There is currently a deficiency in sword 1.6.1 in that backend->setCipherKey() does
225 * not work correctly for modules from which data was already fetched. Therefore we have to
226 * reload the modules.
227 */
228 {
229 const QString moduleName(module->name());
230 CSwordBackend *backend = CSwordBackend::instance();
231 backend->reloadModules(CSwordBackend::OtherChange);
232 module = backend->findModuleByName(moduleName);
233 BT_ASSERT(module);
234 }
235
236 if (!module->isLocked()) break;
237 message::showWarning(parent, tr("Warning: Invalid unlock key!"),
238 tr("The unlock key you provided did not properly unlock this "
239 "module. Please try again."));
240 }
241 return true;
242 }
243
slotModuleUnlock(CSwordModuleInfo * module)244 void BibleTime::slotModuleUnlock(CSwordModuleInfo *module) {
245 moduleUnlock(module, this);
246 }
247
moduleAbout(CSwordModuleInfo * module)248 void BibleTime::moduleAbout(CSwordModuleInfo *module) {
249 BTAboutModuleDialog *dialog = new BTAboutModuleDialog(module, this);
250 dialog->setAttribute(Qt::WA_DeleteOnClose); // Destroy dialog when closed
251 dialog->show();
252 dialog->raise();
253 }
254
255 /** Refreshes all presenters.*/
refreshDisplayWindows() const256 void BibleTime::refreshDisplayWindows() const {
257 Q_FOREACH(QMdiSubWindow const * const subWindow, m_mdi->subWindowList())
258 if (CDisplayWindow * const window =
259 dynamic_cast<CDisplayWindow*>(subWindow->widget()))
260 window->reload(CSwordBackend::OtherChange);
261 }
262
closeEvent(QCloseEvent * event)263 void BibleTime::closeEvent(QCloseEvent *event) {
264 /*
265 Sequentially queries all open subwindows whether its fine to close them. If some sub-
266 window returns false, the querying is stopped and the close event is ignored. If all
267 subwindows return true, the close event is accepted.
268 */
269 Q_FOREACH (QMdiSubWindow * const subWindow, m_mdi->subWindowList()) {
270 if (CDisplayWindow * const window = dynamic_cast<CDisplayWindow*>(subWindow->widget())) {
271 if (!window->queryClose()) {
272 event->ignore();
273 return;
274 }
275 }
276 }
277 event->accept();
278 }
279
processCommandline(bool ignoreSession,const QString & bibleKey)280 void BibleTime::processCommandline(bool ignoreSession, const QString &bibleKey) {
281 if (btConfig().value<bool>("state/crashedTwoTimes", false)) {
282 return;
283 }
284
285 // Restore workspace if not not ignoring session data:
286 if (!ignoreSession)
287 reloadProfile();
288
289 if (btConfig().value<bool>("state/crashedLastTime", false)) {
290 return;
291 }
292
293 if (!bibleKey.isNull()) {
294 CSwordModuleInfo* bible = btConfig().getDefaultSwordModuleByType("standardBible");
295 if (bibleKey == "random") {
296 CSwordVerseKey vk(nullptr);
297 const int maxIndex = 31100;
298 int newIndex = rand() % maxIndex;
299 vk.setPosition(sword::TOP);
300 vk.setIndex(newIndex);
301 createReadDisplayWindow(bible, vk.key());
302 } else {
303 createReadDisplayWindow(bible, bibleKey);
304 }
305
306 /*
307 We are sure only one window is open - it should be displayed
308 fullscreen in the working area:
309 */
310 m_mdi->myTileVertical();
311 }
312
313 if (btConfig().value<bool>("state/crashedLastTime", false)) {
314 btConfig().setValue("state/crashedTwoTimes", true);
315 }
316 else {
317 btConfig().setValue("state/crashedLastTime", true);
318 }
319 btConfig().sync();
320 }
321
event(QEvent * event)322 bool BibleTime::event(QEvent* event) {
323 if (event->type() == QEvent::Close)
324 Search::CSearchDialog::closeDialog();
325 return QMainWindow::event(event);
326 }
327
getCurrentModule()328 const CSwordModuleInfo* BibleTime::getCurrentModule() {
329 QMdiSubWindow* activeSubWindow = m_mdi->activeSubWindow();
330 if (!activeSubWindow)
331 return nullptr;
332 CDisplayWindow* w = dynamic_cast<CDisplayWindow*>(activeSubWindow->widget());
333 if (!w)
334 return nullptr;
335 return w->modules().first();
336 }
337
openFindWidget()338 void BibleTime::openFindWidget()
339 {
340 m_findWidget->setVisible(true);
341 m_findWidget->showAndSelect();
342 }
343