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 <QApplication>
13 #include <QDebug>
14 #include <QDockWidget>
15 #include <QLabel>
16 #include <QMenu>
17 #include <QMenuBar>
18 #include <QPointer>
19 #include <QSplitter>
20 #include <QToolBar>
21 #include <QToolButton>
22 #include <QVBoxLayout>
23 #include "backend/config/btconfig.h"
24 #include "backend/managers/btstringmgr.h"
25 #include "backend/managers/clanguagemgr.h"
26 #include "backend/managers/cswordbackend.h"
27 #include "bibletimeapp.h"
28 #include "frontend/btbookshelfdockwidget.h"
29 #include "frontend/btopenworkaction.h"
30 #include "frontend/cinfodisplay.h"
31 #include "frontend/cmdiarea.h"
32 #include "frontend/display/btfindwidget.h"
33 #include "frontend/displaywindow/btactioncollection.h"
34 #include "frontend/displaywindow/btmodulechooserbar.h"
35 #include "frontend/keychooser/ckeychooser.h"
36 #include "frontend/bookmarks/cbookmarkindex.h"
37 #include "frontend/settingsdialogs/cdisplaysettings.h"
38 #include "util/btassert.h"
39 #include "util/btconnect.h"
40 #include "util/cresmgr.h"
41 #include "util/directory.h"
42 
43 // Sword includes:
44 #include <swlog.h>
45 #include <swmgr.h>
46 
47 #ifndef NDEBUG
48 #include <QLabel>
49 #include <QMetaObject>
50 #include <QMutexLocker>
51 #include <QTimer>
52 #endif
53 
54 
55 using namespace InfoDisplay;
56 
57 /**Initializes the view of this widget*/
initView()58 void BibleTime::initView() {
59 
60     // Create menu and toolbar before the mdi area
61     createMenuAndToolBar();
62 
63     createCentralWidget();
64 
65     m_bookshelfDock = new BtBookshelfDockWidget(this);
66     addDockWidget(Qt::LeftDockWidgetArea, m_bookshelfDock);
67 
68     m_bookmarksDock = new QDockWidget(this);
69     m_bookmarksDock->setObjectName("BookmarksDock");
70     m_bookmarksPage = new CBookmarkIndex(nullptr);
71     m_bookmarksDock->setWidget(m_bookmarksPage);
72     addDockWidget(Qt::LeftDockWidgetArea, m_bookmarksDock);
73     tabifyDockWidget(m_bookmarksDock, m_bookshelfDock);
74 
75     m_magDock = new QDockWidget(this);
76     m_magDock->setObjectName("MagDock");
77     m_infoDisplay = new CInfoDisplay(this);
78     m_infoDisplay->resize(150, 150);
79     m_magDock->setWidget(m_infoDisplay);
80     addDockWidget(Qt::LeftDockWidgetArea, m_magDock);
81 
82     BT_CONNECT(m_bookshelfDock, SIGNAL(moduleHovered(CSwordModuleInfo *)),
83                m_infoDisplay,   SLOT(setInfo(CSwordModuleInfo *)));
84 
85     m_mdi->setMinimumSize(100, 100);
86     m_mdi->setFocusPolicy(Qt::ClickFocus);
87 }
88 
initAction(QAction * action,QString text,QIcon const & icon,QKeySequence accel,const QString & tooltip,const QString & actionName,const char * slot)89 QAction* BibleTime::initAction(QAction* action, QString text, QIcon const & icon,
90                                QKeySequence accel, const QString& tooltip,
91                                const QString& actionName, const char* slot) {
92     action->setText(text);
93     action->setIcon(icon);
94     action->setShortcut(accel);
95     if (tooltip != QString::null)
96         action->setToolTip(tooltip);
97     m_actionCollection->addAction(actionName, action);
98     if (slot)
99         BT_CONNECT(action, SIGNAL(triggered()), this, slot);
100     return action;
101 }
102 
103 // Creates QAction's for all actions that can have keyboard shortcuts
104 // Used in creating the main window and by the configuration dialog for setting shortcuts
insertKeyboardActions(BtActionCollection * const a)105 void BibleTime::insertKeyboardActions( BtActionCollection* const a ) {
106     QAction* action = new QAction(a);
107     action->setText(tr("&Quit"));
108     action->setIcon(CResMgr::mainMenu::window::quit::icon());
109     action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
110     action->setToolTip(tr("Quit BibleTime"));
111     a->addAction("quit", action);
112 
113     action = new QAction(a);
114     action->setText(tr("&Fullscreen mode"));
115     action->setIcon(CResMgr::mainMenu::window::showFullscreen::icon());
116     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::showFullscreen::accel));
117     action->setToolTip(tr("Toggle fullscreen mode of the main window"));
118     a->addAction("toggleFullscreen", action);
119 
120     action = new QAction(a);
121     action->setText(tr("&Show toolbar"));
122     action->setShortcut(QKeySequence(Qt::Key_F6));
123     a->addAction("showToolbar", action);
124 
125     action = new QAction(a);
126     action->setText(tr("Search in &open works..."));
127     action->setIcon(CResMgr::mainMenu::mainIndex::search::icon());
128     action->setShortcut(QKeySequence(CResMgr::mainMenu::mainIndex::search::accel));
129     action->setToolTip(tr("Search in all works that are currently open"));
130     a->addAction("searchOpenWorks", action);
131 
132     action = new QAction(a);
133     action->setText(tr("Search in standard &Bible..."));
134     action->setIcon(CResMgr::mainMenu::mainIndex::searchdefaultbible::icon());
135     action->setShortcut(QKeySequence(CResMgr::mainMenu::mainIndex::searchdefaultbible::accel));
136     action->setToolTip(tr("Search in the standard Bible"));
137     a->addAction("searchStdBible", action);
138 
139     action = new QAction(a);
140     action->setText(tr("Save as &new session..."));
141     action->setIcon(CResMgr::mainMenu::window::saveToNewProfile::icon());
142     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::saveToNewProfile::accel));
143     action->setToolTip(tr("Create and save a new session"));
144     a->addAction("saveNewSession", action);
145 
146     action = new QAction(a);
147     action->setText(tr("&Manual mode"));
148     action->setIcon(CResMgr::mainMenu::window::arrangementMode::manual::icon());
149     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::arrangementMode::manual::accel));
150     action->setToolTip(tr("Manually arrange the open windows"));
151     a->addAction("manualArrangement", action);
152 
153     action = new QAction(a);
154     action->setText(tr("Auto-tile &vertically"));
155     action->setIcon(CResMgr::mainMenu::window::arrangementMode::autoTileVertical::icon());
156     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::arrangementMode::autoTileVertical::accel));
157     action->setToolTip(tr("Automatically tile the open windows vertically (arrange side by side)"));
158     a->addAction("autoVertical", action);
159 
160     action = new QAction(a);
161     action->setText(tr("Auto-tile &horizontally"));
162     action->setIcon(CResMgr::mainMenu::window::arrangementMode::autoTileHorizontal::icon());
163     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::arrangementMode::autoTileHorizontal::accel));
164     action->setToolTip(tr("Automatically tile the open windows horizontally (arrange on top of each other)"));
165     a->addAction("autoHorizontal", action);
166 
167     action = new QAction(a);
168     action->setText(tr("Auto-&tile"));
169     action->setIcon(CResMgr::mainMenu::window::arrangementMode::autoTile::icon());
170     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::arrangementMode::autoTile::accel));
171     action->setToolTip(tr("Automatically tile the open windows"));
172     a->addAction("autoTile", action);
173 
174     action = new QAction(a);
175     action->setText(tr("Ta&bbed"));
176     action->setIcon(CResMgr::mainMenu::window::arrangementMode::autoTabbed::icon());
177     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::arrangementMode::autoTabbed::accel));
178     action->setToolTip(tr("Automatically tab the open windows"));
179     a->addAction("autoTabbed", action);
180 
181     action = new QAction(a);
182     action->setText(tr("Auto-&cascade"));
183     action->setIcon(CResMgr::mainMenu::window::arrangementMode::autoCascade::icon());
184     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::arrangementMode::autoCascade::accel));
185     action->setToolTip(tr("Automatically cascade the open windows"));
186     a->addAction("autoCascade", action);
187 
188     action = new QAction(a);
189     action->setText(tr("&Cascade"));
190     action->setIcon(CResMgr::mainMenu::window::cascade::icon());
191     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::cascade::accel));
192     action->setToolTip(tr("Cascade the open windows"));
193     a->addAction("cascade", action);
194 
195     action = new QAction(a);
196     action->setText(tr("&Tile"));
197     action->setIcon(CResMgr::mainMenu::window::tile::icon());
198     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::tile::accel));
199     action->setToolTip(tr("Tile the open windows"));
200     a->addAction("tile", action);
201 
202     action = new QAction(a);
203     action->setText(tr("Tile &vertically"));
204     action->setIcon(CResMgr::mainMenu::window::tileVertical::icon());
205     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::tileVertical::accel));
206     action->setToolTip(tr("Vertically tile (arrange side by side) the open windows"));
207     a->addAction("tileVertically", action);
208 
209     action = new QAction(a);
210     action->setText(tr("Tile &horizontally"));
211     action->setIcon(CResMgr::mainMenu::window::tileHorizontal::icon());
212     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::tileHorizontal::accel));
213     action->setToolTip(tr("Horizontally tile (arrange on top of each other) the open windows"));
214     a->addAction("tileHorizontally", action);
215 
216     action = new QAction(a);
217     action->setText(tr("Close &window"));
218     action->setIcon(CResMgr::mainMenu::window::close::icon());
219     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::close::accel));
220     action->setToolTip(tr("Close the current open window"));
221     a->addAction("closeWindow", action);
222 
223     action = new QAction(a);
224     action->setText(tr("Cl&ose all windows"));
225     action->setIcon(CResMgr::mainMenu::window::closeAll::icon());
226     action->setShortcut(QKeySequence(CResMgr::mainMenu::window::closeAll::accel));
227     action->setToolTip(tr("Close all open windows inside BibleTime"));
228     a->addAction("closeAllWindows", action);
229 
230     action = new QAction(a);
231     action->setText(tr("&Configure BibleTime..."));
232     action->setIcon(CResMgr::mainMenu::settings::configureDialog::icon());
233     action->setToolTip(tr("Set BibleTime's preferences"));
234     a->addAction("setPreferences", action);
235 
236     action = new QAction(a);
237     action->setText(tr("Bookshelf Manager..."));
238     action->setIcon(CResMgr::mainMenu::settings::swordSetupDialog::icon());
239     action->setShortcut(QKeySequence(CResMgr::mainMenu::settings::swordSetupDialog::accel));
240     action->setToolTip(tr("Configure your bookshelf and install/update/remove/index works"));
241     a->addAction("bookshelfWizard", action);
242 
243     action = new QAction(a);
244     action->setText(tr("&Handbook"));
245     action->setIcon(CResMgr::mainMenu::help::handbook::icon());
246     action->setShortcut(QKeySequence(CResMgr::mainMenu::help::handbook::accel));
247     action->setToolTip(tr("Open BibleTime's handbook"));
248     a->addAction("openHandbook", action);
249 
250     action = new QAction(a);
251     action->setText(tr("&Bible Study Howto"));
252     action->setIcon(CResMgr::mainMenu::help::bibleStudyHowTo::icon());
253     action->setShortcut(QKeySequence(CResMgr::mainMenu::help::bibleStudyHowTo::accel));
254     action->setToolTip(tr("Open the Bible study HowTo included with BibleTime.<br/>This HowTo is an introduction on how to study the Bible in an efficient way."));
255     a->addAction("bibleStudyHowto", action);
256 
257     action = new QAction(a);
258     action->setText(tr("&About BibleTime"));
259     action->setIcon(CResMgr::mainMenu::help::aboutBibleTime::icon());
260     action->setToolTip(tr("Information about the BibleTime program"));
261     a->addAction("aboutBibleTime", action);
262 
263     action = new QAction(a);
264     action->setText(tr("&Tip of the day..."));
265     action->setIcon(CResMgr::mainMenu::help::tipOfTheDay::icon());
266     action->setShortcut(QKeySequence(CResMgr::mainMenu::help::tipOfTheDay::accel));
267     action->setToolTip(tr("Show tips about BibleTime"));
268     a->addAction("tipOfTheDay", action);
269 
270     action = new QAction(a);
271     a->addAction("showToolbarsInTextWindows", action);
272 
273     action = new QAction(a);
274     a->addAction("showNavigation", action);
275 
276     action = new QAction(a);
277     a->addAction("showWorks", action);
278 
279     action = new QAction(a);
280     a->addAction("showTools", action);
281 
282     action = new QAction(a);
283     a->addAction("showFormat", action);
284 
285     action = new QAction(a);
286     a->addAction("showParallelTextHeaders", action);
287 
288     action = new QAction(a);
289     a->addAction("showBookshelf", action);
290 
291     action = new QAction(a);
292     a->addAction("showBookmarks", action);
293 
294     action = new QAction(a);
295     a->addAction("showMag", action);
296 
297     retranslateUiActions(a);
298 }
299 
createToolBar(const QString & name,QWidget * parent,bool visible)300 static QToolBar* createToolBar(const QString& name, QWidget* parent, bool visible) {
301     QToolBar* bar = new QToolBar(parent);
302     bar->setObjectName(name);
303     bar->setFloatable(false);
304     bar->setMovable(true);
305     bar->setVisible(visible);
306     return bar;
307 }
308 
clearMdiToolBars()309 void BibleTime::clearMdiToolBars() {
310     // Clear main window toolbars
311     m_navToolBar->clear();
312     m_worksToolBar->clear();
313     m_toolsToolBar->clear();
314     m_formatToolBar->clear();
315 }
316 
keyChooser() const317 CKeyChooser* BibleTime::keyChooser() const {
318     BT_ASSERT(m_navToolBar);
319 
320     CKeyChooser* keyChooser = m_navToolBar->findChild<CKeyChooser*>();
321     return keyChooser;
322 }
323 
createMenuAndToolBar()324 void BibleTime::createMenuAndToolBar()
325 {
326     // Create menubar
327     menuBar();
328 
329     m_mainToolBar = createToolBar("MainToolBar", this, true);
330     addToolBar(m_mainToolBar);
331 
332     // Set visibility of main window toolbars based on config
333     bool visible = ! btConfig().sessionValue<bool>("GUI/showToolbarsInEachWindow", true);
334 
335     m_navToolBar = createToolBar("NavToolBar", this, visible);
336     addToolBar(m_navToolBar);
337 
338     m_worksToolBar = new BtModuleChooserBar(this);
339     m_worksToolBar->setObjectName("WorksToolBar");
340     m_worksToolBar->setVisible(visible);
341     addToolBar(m_worksToolBar);
342 
343     m_toolsToolBar = createToolBar("ToolsToolBar", this, visible);
344     addToolBar(m_toolsToolBar);
345 
346     m_formatToolBar = createToolBar("FormatToolBar", this, visible);
347     addToolBar(m_formatToolBar);
348 }
349 
createCentralWidget()350 void BibleTime::createCentralWidget()
351 {
352     m_mdi = new CMDIArea(this);
353     m_findWidget = new BtFindWidget(this);
354     m_findWidget->setVisible(false);
355 
356     QVBoxLayout* layout = new QVBoxLayout();
357     QMargins margins(0, 0, 0, 0);
358     layout->setContentsMargins(margins);
359     layout->addWidget(m_mdi);
360     layout->addWidget(m_findWidget);
361 
362     QWidget* widget = new QWidget(this);
363     widget->setLayout(layout);
364     setCentralWidget(widget);
365 
366     BT_CONNECT(m_findWidget, SIGNAL(findNext(QString const &,bool)),
367                m_mdi, SLOT(findNextTextInActiveWindow(QString const &, bool)));
368 
369     BT_CONNECT(m_findWidget, SIGNAL(findPrevious(QString const &,bool)),
370                m_mdi,
371                SLOT(findPreviousTextInActiveWindow(QString const &, bool)));
372 
373     BT_CONNECT(m_findWidget, SIGNAL(highlightText(QString const &,bool)),
374                m_mdi, SLOT(highlightTextInActiveWindow(QString const &, bool)));
375 
376     BT_CONNECT(m_mdi, SIGNAL(subWindowActivated(QMdiSubWindow *)),
377                this, SLOT(slotActiveWindowChanged(QMdiSubWindow *)));
378 }
379 
380 /** Initializes the action objects of the GUI */
initActions()381 void BibleTime::initActions() {
382     m_actionCollection = new BtActionCollection(this);
383     insertKeyboardActions(m_actionCollection);
384 
385     // Create the window to signal mapper and connect it up:
386     m_windowMapper = new QSignalMapper(this);
387     BT_CONNECT(m_windowMapper, SIGNAL(mapped(QWidget *)),
388                this,           SLOT(slotSetActiveSubWindow(QWidget *)));
389 
390     // File menu actions:
391     m_openWorkAction = new BtOpenWorkAction("GUI/mainWindow/openWorkAction/grouping", this);
392     BT_CONNECT(m_openWorkAction, SIGNAL(triggered(CSwordModuleInfo *)),
393                this, SLOT(createReadDisplayWindow(CSwordModuleInfo *)));
394 
395     m_quitAction = &m_actionCollection->action("quit");
396     m_quitAction->setMenuRole(QAction::QuitRole);
397     BT_CONNECT(m_quitAction, SIGNAL(triggered()),
398                this,         SLOT(quit()));
399 
400 
401     // View menu actions:
402     m_windowFullscreenAction = &m_actionCollection->action("toggleFullscreen");
403     m_windowFullscreenAction->setCheckable(true);
404     BT_CONNECT(m_windowFullscreenAction, SIGNAL(triggered()),
405                this,                     SLOT(toggleFullscreen()));
406 
407     // Special case these actions, overwrite those already in collection
408     m_showBookshelfAction = m_bookshelfDock->toggleViewAction();
409     m_showBookshelfAction->setIcon(CResMgr::mainMenu::view::showBookshelf::icon());
410     m_showBookshelfAction->setToolTip(tr("Toggle visibility of the bookshelf window"));
411     m_actionCollection->removeAction("showBookshelf");
412     m_actionCollection->addAction("showBookshelf", m_showBookshelfAction);
413     m_showBookmarksAction = m_bookmarksDock->toggleViewAction();
414     m_showBookmarksAction->setIcon(CResMgr::mainMenu::view::showBookmarks::icon());
415     m_showBookmarksAction->setToolTip(tr("Toggle visibility of the bookmarks window"));
416     m_actionCollection->removeAction("showBookmarks");
417     m_actionCollection->addAction("showBookmarks", m_showBookmarksAction);
418     m_showMagAction = m_magDock->toggleViewAction();
419     m_showMagAction->setIcon(CResMgr::mainMenu::view::showMag::icon());
420     m_showMagAction->setToolTip(tr("Toggle visibility of the mag window"));
421     m_actionCollection->removeAction("showMag");
422     m_actionCollection->addAction("showMag", m_showMagAction);
423 
424     m_showTextAreaHeadersAction =
425             &m_actionCollection->action("showParallelTextHeaders");
426     m_showTextAreaHeadersAction->setCheckable(true);
427     m_showTextAreaHeadersAction->setChecked(btConfig().sessionValue<bool>("GUI/showTextWindowHeaders", true));
428     BT_CONNECT(m_showTextAreaHeadersAction, SIGNAL(toggled(bool)),
429                this,                        SLOT(slotToggleTextWindowHeader()));
430 
431     m_showMainWindowToolbarAction = &m_actionCollection->action("showToolbar");
432     m_showMainWindowToolbarAction->setCheckable(true);
433     m_showMainWindowToolbarAction->setChecked(btConfig().sessionValue<bool>("GUI/showMainToolbar", true));
434     BT_CONNECT(m_showMainWindowToolbarAction, SIGNAL(triggered()),
435                this, SLOT(slotToggleMainToolbar()));
436 
437     m_showTextWindowNavigationAction =
438             &m_actionCollection->action("showNavigation");
439     m_showTextWindowNavigationAction->setCheckable(true);
440     m_showTextWindowNavigationAction->setChecked(btConfig().sessionValue<bool>("GUI/showTextWindowNavigator", true));
441     BT_CONNECT(m_showTextWindowNavigationAction, SIGNAL(toggled(bool)),
442                this, SLOT(slotToggleNavigatorToolbar()));
443 
444     m_showTextWindowModuleChooserAction =
445             &m_actionCollection->action("showWorks");
446     m_showTextWindowModuleChooserAction->setCheckable(true);
447     m_showTextWindowModuleChooserAction->setChecked(btConfig().sessionValue<bool>("GUI/showTextWindowModuleSelectorButtons", true));
448     BT_CONNECT(m_showTextWindowModuleChooserAction, SIGNAL(toggled(bool)),
449                this, SLOT(slotToggleWorksToolbar()));
450 
451     m_showTextWindowToolButtonsAction =
452             &m_actionCollection->action("showTools");
453     m_showTextWindowToolButtonsAction->setCheckable(true);
454     m_showTextWindowToolButtonsAction->setChecked(btConfig().sessionValue<bool>("GUI/showTextWindowToolButtons", true));
455     BT_CONNECT(m_showTextWindowToolButtonsAction, SIGNAL(toggled(bool)),
456                this, SLOT(slotToggleToolsToolbar()));
457 
458     m_showFormatToolbarAction = &m_actionCollection->action("showFormat");
459     m_showFormatToolbarAction->setCheckable(true);
460     m_showFormatToolbarAction->setChecked(btConfig().sessionValue<bool>("GUI/showFormatToolbarButtons", true));
461     BT_CONNECT(m_showFormatToolbarAction, SIGNAL(toggled(bool)),
462                this,                      SLOT(slotToggleFormatToolbar()));
463 
464     m_toolbarsInEachWindow =
465             &m_actionCollection->action("showToolbarsInTextWindows");
466     m_toolbarsInEachWindow->setCheckable(true);
467     m_toolbarsInEachWindow->setChecked(btConfig().sessionValue<bool>("GUI/showToolbarsInEachWindow", true));
468     BT_CONNECT(m_toolbarsInEachWindow, SIGNAL(toggled(bool)),
469                this,                   SLOT(slotToggleToolBarsInEachWindow()));
470 
471     // Search menu actions:
472     m_searchOpenWorksAction = &m_actionCollection->action("searchOpenWorks");
473     BT_CONNECT(m_searchOpenWorksAction, SIGNAL(triggered()),
474                this,                    SLOT(slotSearchModules()));
475 
476     m_searchStandardBibleAction = &m_actionCollection->action("searchStdBible");
477     BT_CONNECT(m_searchStandardBibleAction, SIGNAL(triggered()),
478                this,                        SLOT(slotSearchDefaultBible()));
479 
480     // Window menu actions:
481     m_windowCloseAction = &m_actionCollection->action("closeWindow");
482     BT_CONNECT(m_windowCloseAction, SIGNAL(triggered()),
483                m_mdi,               SLOT(closeActiveSubWindow()));
484 
485     m_windowCloseAllAction = &m_actionCollection->action("closeAllWindows");
486     BT_CONNECT(m_windowCloseAllAction, SIGNAL(triggered()),
487                m_mdi,                 SLOT(closeAllSubWindows()));
488 
489     m_windowCascadeAction = &m_actionCollection->action("cascade");
490     BT_CONNECT(m_windowCascadeAction, SIGNAL(triggered()),
491                this,                  SLOT(slotCascade()));
492 
493     m_windowTileAction = &m_actionCollection->action("tile");
494     BT_CONNECT(m_windowTileAction, SIGNAL(triggered()),
495                this,               SLOT(slotTile()));
496 
497     m_windowTileVerticalAction = &m_actionCollection->action("tileVertically");
498     BT_CONNECT(m_windowTileVerticalAction, SIGNAL(triggered()),
499                this,                       SLOT(slotTileVertical()));
500 
501     m_windowTileHorizontalAction =
502             &m_actionCollection->action("tileHorizontally");
503     BT_CONNECT(m_windowTileHorizontalAction, SIGNAL(triggered()),
504                this,                         SLOT(slotTileHorizontal()));
505 
506     alignmentMode alignment = btConfig().sessionValue<alignmentMode>("GUI/alignmentMode", autoTileVertical);
507 
508     m_windowManualModeAction = &m_actionCollection->action("manualArrangement");
509     m_windowManualModeAction->setCheckable(true);
510 
511     m_windowAutoTabbedAction = &m_actionCollection->action("autoTabbed");
512     m_windowAutoTabbedAction->setCheckable(true);
513 
514     //: Vertical tiling means that windows are vertical, placed side by side
515     m_windowAutoTileVerticalAction =
516             &m_actionCollection->action("autoVertical");
517     m_windowAutoTileVerticalAction->setCheckable(true);
518 
519     //: Horizontal tiling means that windows are horizontal, placed on top of each other
520     m_windowAutoTileHorizontalAction =
521             &m_actionCollection->action("autoHorizontal");
522     m_windowAutoTileHorizontalAction->setCheckable(true);
523 
524     m_windowAutoTileAction = &m_actionCollection->action("autoTile");
525     m_windowAutoTileAction->setCheckable(true);
526 
527     m_windowAutoCascadeAction = &m_actionCollection->action("autoCascade");
528     m_windowAutoCascadeAction->setCheckable(true);
529 
530     /*
531      * All actions related to arrangement modes have to be initialized before calling a slot on them,
532      * thus we call them afterwards now.
533      */
534     QAction * alignmentAction;
535     switch (alignment) {
536         case autoTabbed:
537             alignmentAction = m_windowAutoTabbedAction; break;
538         case autoTileVertical:
539             alignmentAction = m_windowAutoTileVerticalAction; break;
540         case autoTileHorizontal:
541             alignmentAction = m_windowAutoTileHorizontalAction; break;
542         case autoTile:
543             alignmentAction = m_windowAutoTileAction; break;
544         case autoCascade:
545             alignmentAction = m_windowAutoCascadeAction; break;
546         case manual:
547         default:
548             alignmentAction = m_windowManualModeAction; break;
549     }
550     alignmentAction->setChecked(true);
551     slotUpdateWindowArrangementActions(alignmentAction);
552 
553     m_windowSaveToNewProfileAction =
554             &m_actionCollection->action("saveNewSession");
555     BT_CONNECT(m_windowSaveToNewProfileAction, SIGNAL(triggered()),
556                this,                           SLOT(saveToNewProfile()));
557 
558     m_setPreferencesAction = &m_actionCollection->action("setPreferences");
559     m_setPreferencesAction->setMenuRole( QAction::PreferencesRole );
560     BT_CONNECT(m_setPreferencesAction, SIGNAL(triggered()),
561                this,                   SLOT(slotSettingsOptions()));
562 
563     m_bookshelfWizardAction = &m_actionCollection->action("bookshelfWizard");
564     m_bookshelfWizardAction->setMenuRole( QAction::ApplicationSpecificRole );
565     BT_CONNECT(m_bookshelfWizardAction, SIGNAL(triggered()),
566                this,                     SLOT(slotBookshelfWizard()));
567 
568     m_openHandbookAction = &m_actionCollection->action("openHandbook");
569     BT_CONNECT(m_openHandbookAction, SIGNAL(triggered()),
570                this,                 SLOT(openOnlineHelp_Handbook()));
571 
572     m_bibleStudyHowtoAction = &m_actionCollection->action("bibleStudyHowto");
573     BT_CONNECT(m_bibleStudyHowtoAction, SIGNAL(triggered()),
574                this,                    SLOT(openOnlineHelp_Howto()));
575 
576     m_aboutBibleTimeAction = &m_actionCollection->action("aboutBibleTime");
577     m_aboutBibleTimeAction->setMenuRole( QAction::AboutRole );
578     BT_CONNECT(m_aboutBibleTimeAction, SIGNAL(triggered()),
579                this,                   SLOT(slotOpenAboutDialog()) );
580 
581     m_tipOfTheDayAction = &m_actionCollection->action("tipOfTheDay");
582     BT_CONNECT(m_tipOfTheDayAction, SIGNAL(triggered()),
583                this,                SLOT(slotOpenTipDialog()) );
584 
585     #ifndef NDEBUG
586     m_debugWidgetAction = new QAction(this);
587     m_debugWidgetAction->setCheckable(true);
588     BT_CONNECT(m_debugWidgetAction, &QAction::triggered,
589                this,                &BibleTime::slotShowDebugWindow);
590     #endif
591 
592     retranslateUiActions(m_actionCollection);
593 }
594 
initMenubar()595 void BibleTime::initMenubar() {
596     // File menu:
597     m_fileMenu = new QMenu(this);
598     m_fileMenu->addAction(m_openWorkAction);
599     m_fileMenu->addSeparator();
600     m_fileMenu->addAction(m_quitAction);
601     menuBar()->addMenu(m_fileMenu);
602 
603     // View menu:
604     m_viewMenu = new QMenu(this);
605     m_viewMenu->addAction(m_windowFullscreenAction);
606     m_viewMenu->addAction(m_showBookshelfAction);
607     m_viewMenu->addAction(m_showBookmarksAction);
608     m_viewMenu->addAction(m_showMagAction);
609     m_viewMenu->addAction(m_showTextAreaHeadersAction);
610     m_viewMenu->addSeparator();
611     m_toolBarsMenu = new QMenu(this);
612     m_toolBarsMenu->addAction( m_showMainWindowToolbarAction);
613     m_toolBarsMenu->addAction(m_showTextWindowNavigationAction);
614     m_toolBarsMenu->addAction(m_showTextWindowModuleChooserAction);
615     m_toolBarsMenu->addAction(m_showTextWindowToolButtonsAction);
616     m_toolBarsMenu->addAction(m_showFormatToolbarAction);
617     m_toolBarsMenu->addSeparator();
618     m_toolBarsMenu->addAction(m_toolbarsInEachWindow);
619     m_viewMenu->addMenu(m_toolBarsMenu);
620     menuBar()->addMenu(m_viewMenu);
621 
622     // Search menu:
623     m_searchMenu = new QMenu(this);
624     m_searchMenu->addAction(m_searchOpenWorksAction);
625     m_searchMenu->addAction(m_searchStandardBibleAction);
626     menuBar()->addMenu(m_searchMenu);
627 
628     // Window menu:
629     m_windowMenu = new QMenu(this);
630     m_openWindowsMenu = new QMenu(this);
631     BT_CONNECT(m_openWindowsMenu, SIGNAL(aboutToShow()),
632                this,              SLOT(slotOpenWindowsMenuAboutToShow()));
633     m_windowMenu->addMenu(m_openWindowsMenu);
634     m_windowMenu->addAction(m_windowCloseAction);
635     m_windowMenu->addAction(m_windowCloseAllAction);
636     m_windowMenu->addSeparator();
637     m_windowMenu->addAction(m_windowCascadeAction);
638     m_windowMenu->addAction(m_windowTileAction);
639     m_windowMenu->addAction(m_windowTileVerticalAction);
640     m_windowMenu->addAction(m_windowTileHorizontalAction);
641     m_windowArrangementMenu = new QMenu(this);
642     m_windowArrangementActionGroup = new QActionGroup(m_windowArrangementMenu);
643     m_windowArrangementMenu->addAction(m_windowManualModeAction);
644     m_windowArrangementActionGroup->addAction(m_windowManualModeAction);
645     m_windowArrangementMenu->addAction(m_windowAutoTabbedAction);
646     m_windowArrangementActionGroup->addAction(m_windowAutoTabbedAction);
647     m_windowArrangementMenu->addAction(m_windowAutoTileVerticalAction);
648     m_windowArrangementActionGroup->addAction(m_windowAutoTileVerticalAction);
649     m_windowArrangementMenu->addAction(m_windowAutoTileHorizontalAction);
650     m_windowArrangementActionGroup->addAction(m_windowAutoTileHorizontalAction);
651     m_windowArrangementMenu->addAction(m_windowAutoTileAction);
652     m_windowArrangementActionGroup->addAction(m_windowAutoTileAction);
653     m_windowArrangementMenu->addAction(m_windowAutoCascadeAction);
654     m_windowArrangementActionGroup->addAction(m_windowAutoCascadeAction);
655     BT_CONNECT(m_windowArrangementActionGroup, SIGNAL(triggered(QAction *)),
656                this, SLOT(slotUpdateWindowArrangementActions(QAction *)));
657 
658     m_windowMenu->addMenu(m_windowArrangementMenu);
659     m_windowMenu->addSeparator();
660     m_windowMenu->addAction(m_windowSaveToNewProfileAction);
661     m_windowLoadProfileMenu = new QMenu(this);
662     m_windowLoadProfileActionGroup = new QActionGroup(m_windowLoadProfileMenu);
663     m_windowMenu->addMenu(m_windowLoadProfileMenu);
664     m_windowDeleteProfileMenu = new QMenu(this);
665     m_windowMenu->addMenu(m_windowDeleteProfileMenu);
666     BT_CONNECT(m_windowLoadProfileMenu, SIGNAL(triggered(QAction *)),
667                this,                    SLOT(loadProfile(QAction *)));
668     BT_CONNECT(m_windowDeleteProfileMenu, SIGNAL(triggered(QAction *)),
669                this,                      SLOT(deleteProfile(QAction *)));
670     refreshProfileMenus();
671     menuBar()->addMenu(m_windowMenu);
672     BT_CONNECT(m_windowMenu, SIGNAL(aboutToShow()),
673                this,         SLOT(slotWindowMenuAboutToShow()));
674 
675     #ifndef Q_OS_MAC
676     m_settingsMenu = new QMenu(this);
677     m_settingsMenu->addAction(m_setPreferencesAction);
678     m_settingsMenu->addSeparator();
679     m_settingsMenu->addAction(m_bookshelfWizardAction);
680     menuBar()->addMenu(m_settingsMenu);
681     #else
682     // On MAC OS, the settings actions will be moved to a system menu item.
683     // Therefore the settings menu would be empty, so we do not show it.
684     m_fileMenu->addAction(m_setPreferencesAction);
685     m_fileMenu->addAction(m_bookshelfWizardAction);
686     #endif
687 
688     // Help menu:
689     m_helpMenu = new QMenu(this);
690     m_helpMenu->addAction(m_openHandbookAction);
691     m_helpMenu->addAction(m_bibleStudyHowtoAction);
692     m_helpMenu->addAction(m_tipOfTheDayAction);
693     m_helpMenu->addSeparator();
694     m_helpMenu->addAction(m_aboutBibleTimeAction);
695     #ifndef NDEBUG
696     m_helpMenu->addSeparator();
697     m_helpMenu->addAction(m_debugWidgetAction);
698     #endif
699     menuBar()->addMenu(m_helpMenu);
700 }
701 
initToolbars()702 void BibleTime::initToolbars() {
703     QToolButton *openWorkButton = new QToolButton(this);
704     openWorkButton->setDefaultAction(m_openWorkAction);
705     openWorkButton->setPopupMode(QToolButton::InstantPopup);
706     m_mainToolBar->addWidget(openWorkButton);
707 
708     m_mainToolBar->addAction(m_windowFullscreenAction);
709     m_mainToolBar->addAction(&m_actionCollection->action("showBookshelf"));
710     m_mainToolBar->addAction(&m_actionCollection->action("showBookmarks"));
711     m_mainToolBar->addAction(&m_actionCollection->action("showMag"));
712     m_mainToolBar->addAction(m_searchOpenWorksAction);
713     m_mainToolBar->addAction(m_openHandbookAction);
714 }
715 
retranslateUi()716 void BibleTime::retranslateUi() {
717     m_bookmarksDock->setWindowTitle(tr("Bookmarks"));
718     m_magDock->setWindowTitle(tr("Mag"));
719     m_mainToolBar->setWindowTitle(tr("Main toolbar"));
720     m_navToolBar->setWindowTitle(tr("Navigation toolbar"));
721     m_worksToolBar->setWindowTitle(tr("Works toolbar"));
722     m_toolsToolBar->setWindowTitle(tr("Tools toolbar"));
723     m_formatToolBar->setWindowTitle(tr("Format toolbar"));
724 
725     m_fileMenu->setTitle(tr("&File"));
726     m_viewMenu->setTitle(tr("&View"));
727     m_toolBarsMenu->setTitle(tr("Toolbars"));
728 
729     m_searchMenu->setTitle(tr("&Search"));
730     m_windowMenu->setTitle(tr("&Window"));
731     m_openWindowsMenu->setTitle(tr("O&pen windows"));
732     m_windowArrangementMenu->setTitle(tr("&Arrangement mode"));
733     m_windowLoadProfileMenu->setTitle(tr("Sw&itch session"));
734     m_windowDeleteProfileMenu->setTitle(tr("&Delete session"));
735 
736     #ifndef Q_OS_MAC
737     // This item is not present on Mac OS
738     m_settingsMenu->setTitle(tr("Se&ttings"));
739     #endif
740 
741     m_helpMenu->setTitle(tr("&Help"));
742 
743     #ifndef NDEBUG
744     m_debugWidgetAction->setText(tr("Show \"Whats this widget\" dialog"));
745     #endif
746 
747     retranslateUiActions(m_actionCollection);
748 }
749 
750 /** retranslation for actions used in this class
751 *   This is called for two different collections of actions
752 *   One set is for the actual use in the menus, etc
753 *   The second is used during the use of the configuration shortcut editor
754 */
retranslateUiActions(BtActionCollection * ac)755 void BibleTime::retranslateUiActions(BtActionCollection* ac) {
756     ac->action("showToolbarsInTextWindows")
757             .setText(tr("Show toolbars in text windows"));
758     ac->action("showToolbar").setText(tr("Show main toolbar"));
759     ac->action("showNavigation").setText(tr("Show navigation bar"));
760     ac->action("showWorks").setText(tr("Show works toolbar"));
761     ac->action("showTools").setText(tr("Show tools toolbar"));
762     ac->action("showFormat").setText(tr("Show formatting toolbar"));
763     ac->action("showBookshelf").setText(tr("Show bookshelf"));
764     ac->action("showBookmarks").setText(tr("Show bookmarks"));
765     ac->action("showMag").setText(tr("Show mag"));
766     ac->action("showParallelTextHeaders")
767             .setText(tr("Show parallel text headers"));
768 }
769 
770 /** Initializes the SIGNAL / SLOT connections */
initConnections()771 void BibleTime::initConnections() {
772     // Bookmarks page connections:
773     BT_CONNECT(m_bookmarksPage,
774                SIGNAL(createReadDisplayWindow(QList<CSwordModuleInfo *>,
775                                               QString const &)),
776                this,
777                SLOT(createReadDisplayWindow(QList<CSwordModuleInfo *>,
778                                             QString const &)));
779 
780     // Bookshelf dock connections:
781     BT_CONNECT(m_bookshelfDock, SIGNAL(moduleOpenTriggered(CSwordModuleInfo *)),
782                this, SLOT(createReadDisplayWindow(CSwordModuleInfo *)));
783     BT_CONNECT(m_bookshelfDock,
784                SIGNAL(moduleSearchTriggered(CSwordModuleInfo *)),
785                this, SLOT(searchInModule(CSwordModuleInfo *)));
786     BT_CONNECT(m_bookshelfDock,
787                SIGNAL(moduleEditPlainTriggered(CSwordModuleInfo *)),
788                this, SLOT(moduleEditPlain(CSwordModuleInfo *)));
789     BT_CONNECT(m_bookshelfDock,
790                SIGNAL(moduleEditHtmlTriggered(CSwordModuleInfo *)),
791                this, SLOT(moduleEditHtml(CSwordModuleInfo *)));
792     BT_CONNECT(m_bookshelfDock,
793                SIGNAL(moduleUnlockTriggered(CSwordModuleInfo *)),
794                this, SLOT(slotModuleUnlock(CSwordModuleInfo *)));
795     BT_CONNECT(m_bookshelfDock,
796                SIGNAL(moduleAboutTriggered(CSwordModuleInfo *)),
797                this, SLOT(moduleAbout(CSwordModuleInfo *)));
798 }
799 
initSwordConfigFile()800 void BibleTime::initSwordConfigFile() {
801 // On Windows the sword.conf must be created before the initialization of sword
802 // It will contain the LocalePath which is used for sword locales
803 // It also contains a DataPath to the %ProgramData%\Sword directory
804 // If this is not done here, the sword locales.d won't be found
805 #ifdef Q_OS_WIN
806     QString configFile = util::directory::getUserHomeSwordDir().filePath("sword.conf");
807     QFile file(configFile);
808     if (file.exists()) {
809         return;
810     }
811     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
812         return;
813     }
814     QTextStream out(&file);
815     out << "\n";
816     out << "[Install]\n";
817     out << "DataPath="   << util::directory::convertDirSeparators( util::directory::getSharedSwordDir().absolutePath()) << "\n";
818     out << "LocalePath=" << util::directory::convertDirSeparators(util::directory::getApplicationSwordDir().absolutePath()) << "\n";
819     out << "\n";
820     file.close();
821 #endif
822 
823 #ifdef Q_OS_MAC
824     QString configFile = util::directory::getUserHomeSwordDir().filePath("sword.conf");
825     QFile file(configFile);
826     if (file.exists()) {
827         return;
828     }
829     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
830         return;
831     }
832     QTextStream out(&file);
833     out << "\n";
834     out << "[Install]\n";
835     out << "DataPath="   << util::directory::convertDirSeparators( util::directory::getUserHomeSwordDir().absolutePath()) << "\n";
836     out << "\n";
837     file.close();
838 #endif
839 }
840 
841 /** Initializes the backend */
initBackends()842 void BibleTime::initBackends() {
843     initSwordConfigFile();
844 
845     if (!sword::SWMgr::isICU)
846         sword::StringMgr::setSystemStringMgr(new BtStringMgr());
847 
848     sword::SWLog::getSystemLog()->setLogLevel(btApp->debugMode()
849                                               ? sword::SWLog::LOG_DEBUG
850                                               : sword::SWLog::LOG_ERROR);
851 
852 #ifdef Q_OS_MAC
853     // set a LocaleMgr with a fixed path to the locales.d of the DMG image on MacOS
854     // note: this must be done after setting the BTStringMgr, because this will reset the LocaleMgr
855     qDebug() << "Using sword locales dir: " << util::directory::getSwordLocalesDir().absolutePath().toUtf8();
856     sword::LocaleMgr::setSystemLocaleMgr(new sword::LocaleMgr(util::directory::getSwordLocalesDir().absolutePath().toUtf8()));
857 #endif
858 
859     /*
860       Set book names language if not set. This is a hack. We do this call here,
861       because we need to keep the setting displayed in BtLanguageSettingsPage in
862       sync with the language of the book names displayed, so that both would
863       always use the same setting.
864     */
865     CDisplaySettingsPage::resetLanguage(); /// \todo refactor this hack
866 
867 
868     CSwordBackend *backend = CSwordBackend::createInstance();
869     backend->booknameLanguage(btConfig().value<QString>("GUI/booknameLanguage", QLocale::system().name()));
870 
871     CSwordBackend::instance()->initModules(CSwordBackend::OtherChange);
872 
873     // This function will
874     // - delete all orphaned indexes (no module present) if autoDeleteOrphanedIndices is true
875     // - delete all indices of modules where hasIndex() returns false
876     backend->deleteOrphanedIndices();
877 
878 }
879 
880 #ifndef NDEBUG
881 
882 QLabel *BibleTime::m_debugWindow = nullptr;
883 QMutex BibleTime::m_debugWindowLock;
884 
slotShowDebugWindow(bool show)885 void BibleTime::slotShowDebugWindow(bool show) {
886     if (show) {
887         QMutexLocker lock(&m_debugWindowLock);
888         if (m_debugWindow == nullptr) {
889             m_debugWindow = new QLabel(nullptr, Qt::Dialog);
890             m_debugWindow->setAttribute(Qt::WA_DeleteOnClose);
891             m_debugWindow->setTextFormat(Qt::RichText);
892             m_debugWindow->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
893             m_debugWindow->setWindowTitle(tr("Whats this widget?"));
894         }
895         m_debugWindow->show();
896         BT_CONNECT(m_debugWindow, &QObject::destroyed,
897                    this,          &BibleTime::slotDebugWindowClosing,
898                    Qt::DirectConnection);
899         #if QT_VERSION < 0x050400
900         QTimer::singleShot(0, this, SLOT(slotDebugTimeout()));
901         #else
902         QTimer::singleShot(0, this, &BibleTime::slotDebugTimeout);
903         #endif
904     } else {
905         deleteDebugWindow();
906     }
907 }
908 
deleteDebugWindow()909 void BibleTime::deleteDebugWindow() {
910     QMutexLocker lock(&m_debugWindowLock);
911     if (m_debugWindow != nullptr) {
912         disconnect(m_debugWindow, &QObject::destroyed,
913                    this,          &BibleTime::slotDebugWindowClosing);
914         delete m_debugWindow;
915         m_debugWindow = nullptr;
916     }
917 }
918 
slotDebugWindowClosing()919 void BibleTime::slotDebugWindowClosing() {
920     QMutexLocker lock(&m_debugWindowLock);
921     m_debugWindow = nullptr;
922     m_debugWidgetAction->setChecked(false);
923 }
924 
slotDebugTimeout()925 void BibleTime::slotDebugTimeout() {
926     QMutexLocker lock(&m_debugWindowLock);
927     if (!m_debugWindow || m_debugWindow->isVisible() == false)
928         return;
929     #if QT_VERSION < 0x050400
930     QTimer::singleShot(0, this, SLOT(slotDebugTimeout()));
931     #else
932     QTimer::singleShot(0, this, &BibleTime::slotDebugTimeout);
933     #endif
934     if (QObject const * w = QApplication::widgetAt(QCursor::pos())) {
935         QString objectHierarchy;
936         do {
937             QMetaObject const * m = w->metaObject();
938             QString classHierarchy;
939             do {
940                 if (!classHierarchy.isEmpty())
941                     classHierarchy += ": ";
942                 classHierarchy += m->className();
943                 m = m->superClass();
944             } while (m);
945             if (!objectHierarchy.isEmpty()) {
946                 objectHierarchy += "<br/><b>child of:</b> ";
947             } else {
948                 objectHierarchy += "<b>This widget is:</b> ";
949             }
950             objectHierarchy += classHierarchy;
951             w = w->parent();
952         } while (w);
953         m_debugWindow->setText(objectHierarchy);
954     } else {
955         m_debugWindow->setText("No widget");
956     }
957     m_debugWindow->resize(m_debugWindow->minimumSizeHint());
958 }
959 
960 #endif
961