1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Assistant module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "openpagesmanager.h"
30 
31 #include "centralwidget.h"
32 #include "helpenginewrapper.h"
33 #include "helpviewer.h"
34 #include "openpagesmodel.h"
35 #include "openpagesswitcher.h"
36 #include "openpageswidget.h"
37 #include "tracer.h"
38 #include "../shared/collectionconfiguration.h"
39 
40 #include <QtWidgets/QApplication>
41 #include <QtWidgets/QTreeView>
42 
43 QT_BEGIN_NAMESPACE
44 
45 OpenPagesManager *OpenPagesManager::m_instance = nullptr;
46 
createInstance(QObject * parent,bool defaultCollection,const QUrl & cmdLineUrl)47 OpenPagesManager *OpenPagesManager::createInstance(QObject *parent,
48                       bool defaultCollection, const QUrl &cmdLineUrl)
49 {
50     TRACE_OBJ
51     Q_ASSERT(!m_instance);
52     m_instance = new OpenPagesManager(parent, defaultCollection, cmdLineUrl);
53     return m_instance;
54 }
55 
instance()56 OpenPagesManager *OpenPagesManager::instance()
57 {
58     TRACE_OBJ
59     Q_ASSERT(m_instance);
60     return m_instance;
61 }
62 
OpenPagesManager(QObject * parent,bool defaultCollection,const QUrl & cmdLineUrl)63 OpenPagesManager::OpenPagesManager(QObject *parent, bool defaultCollection,
64                                    const QUrl &cmdLineUrl)
65     : QObject(parent)
66     , m_model(new OpenPagesModel(this))
67 {
68     TRACE_OBJ
69     m_openPagesWidget = new OpenPagesWidget(m_model);
70     m_openPagesWidget->setFrameStyle(QFrame::NoFrame);
71     connect(m_openPagesWidget, &OpenPagesWidget::setCurrentPage,
72             this, QOverload<const QModelIndex &>::of(&OpenPagesManager::setCurrentPage));
73     connect(m_openPagesWidget, &OpenPagesWidget::closePage,
74             this, QOverload<const QModelIndex &>::of(&OpenPagesManager::closePage));
75     connect(m_openPagesWidget, &OpenPagesWidget::closePagesExcept,
76             this, &OpenPagesManager::closePagesExcept);
77 
78     m_openPagesSwitcher = new OpenPagesSwitcher(m_model);
79     connect(m_openPagesSwitcher, &OpenPagesSwitcher::closePage,
80             this, QOverload<const QModelIndex &>::of(&OpenPagesManager::closePage));
81     connect(m_openPagesSwitcher, &OpenPagesSwitcher::setCurrentPage,
82             this, QOverload<const QModelIndex &>::of(&OpenPagesManager::setCurrentPage));
83 
84     setupInitialPages(defaultCollection, cmdLineUrl);
85 }
86 
~OpenPagesManager()87 OpenPagesManager ::~OpenPagesManager()
88 {
89     TRACE_OBJ
90     m_instance = nullptr;
91     delete m_openPagesSwitcher;
92 }
93 
pageCount() const94 int OpenPagesManager::pageCount() const
95 {
96     TRACE_OBJ
97     return m_model->rowCount();
98 }
99 
setupInitialPages(bool defaultCollection,const QUrl & cmdLineUrl)100 void OpenPagesManager::setupInitialPages(bool defaultCollection,
101             const QUrl &cmdLineUrl)
102 {
103     TRACE_OBJ
104     if (cmdLineUrl.isValid()) {
105         createPage(cmdLineUrl);
106         return;
107     }
108 
109     HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance();
110     int initialPage = 0;
111     switch (helpEngine.startOption()) {
112     case ShowHomePage:
113         m_model->addPage(helpEngine.homePage());
114         break;
115     case ShowBlankPage:
116         m_model->addPage(QUrl(QLatin1String("about:blank")));
117         break;
118     case ShowLastPages: {
119         const QStringList &lastShownPageList = helpEngine.lastShownPages();
120         const int pageCount = lastShownPageList.count();
121         if (pageCount == 0) {
122             if (defaultCollection)
123                 m_model->addPage(QUrl(QLatin1String("help")));
124             else
125                 m_model->addPage(QUrl(QLatin1String("about:blank")));
126         } else {
127             QStringList zoomFactors = helpEngine.lastZoomFactors();
128             while (zoomFactors.count() < pageCount)
129                 zoomFactors.append(CollectionConfiguration::DefaultZoomFactor);
130             initialPage = helpEngine.lastTabPage();
131             if (initialPage >= pageCount) {
132                 qWarning("Initial page set to %d, maximum possible value is %d",
133                          initialPage, pageCount - 1);
134                 initialPage = 0;
135             }
136             for (int curPage = 0; curPage < pageCount; ++curPage) {
137                 const QString &curFile = lastShownPageList.at(curPage);
138                 if (helpEngine.findFile(curFile).isValid()
139                     || curFile == QLatin1String("about:blank")) {
140                     m_model->addPage(curFile, zoomFactors.at(curPage).toFloat());
141                 } else if (curPage <= initialPage && initialPage > 0)
142                     --initialPage;
143             }
144         }
145         break;
146     }
147     default:
148         Q_ASSERT(0);
149     }
150 
151     if (m_model->rowCount() == 0)
152         m_model->addPage(helpEngine.homePage());
153     for (int i = 0; i < m_model->rowCount(); ++i)
154         CentralWidget::instance()->addPage(m_model->pageAt(i));
155     setCurrentPage((initialPage >= m_model->rowCount())
156         ? m_model->rowCount() - 1 : initialPage);
157     m_openPagesSwitcher->selectCurrentPage();
158 }
159 
createBlankPage()160 HelpViewer *OpenPagesManager::createBlankPage()
161 {
162     TRACE_OBJ
163     return createPage(QUrl(QLatin1String("about:blank")));
164 }
165 
closeCurrentPage()166 void OpenPagesManager::closeCurrentPage()
167 {
168     TRACE_OBJ
169     Q_ASSERT(m_model->rowCount() > 1);
170     const QModelIndexList selectedIndexes
171         = m_openPagesWidget->selectionModel()->selectedRows();
172     if (selectedIndexes.isEmpty())
173         return;
174     Q_ASSERT(selectedIndexes.count() == 1);
175     removePage(selectedIndexes.first().row());
176 }
177 
createPage(const QUrl & url,bool fromSearch)178 HelpViewer *OpenPagesManager::createPage(const QUrl &url, bool fromSearch)
179 {
180     TRACE_OBJ
181     if (HelpViewer::launchWithExternalApp(url))
182         return nullptr;
183 
184     emit aboutToAddPage();
185 
186     m_model->addPage(url);
187     const int index = m_model->rowCount() - 1;
188     HelpViewer * const page = m_model->pageAt(index);
189     CentralWidget::instance()->addPage(page, fromSearch);
190     setCurrentPage(index);
191 
192     emit pageAdded(index);
193     return page;
194 }
195 
createNewPageFromSearch(const QUrl & url)196 HelpViewer *OpenPagesManager::createNewPageFromSearch(const QUrl &url)
197 {
198     TRACE_OBJ
199     return createPage(url, true);
200 }
201 
closePage(HelpViewer * viewer)202 void OpenPagesManager::closePage(HelpViewer *viewer)
203 {
204     TRACE_OBJ
205     for (int i = 0; i < m_model->rowCount(); ++i) {
206         if (m_model->pageAt(i) == viewer) {
207             removePage(i);
208             break;
209         }
210     }
211 }
212 
closePage(const QModelIndex & index)213 void OpenPagesManager::closePage(const QModelIndex &index)
214 {
215     TRACE_OBJ
216     if (index.isValid())
217         removePage(index.row());
218 }
219 
closePages(const QString & nameSpace)220 void OpenPagesManager::closePages(const QString &nameSpace)
221 {
222     TRACE_OBJ
223     closeOrReloadPages(nameSpace, false);
224 }
225 
reloadPages(const QString & nameSpace)226 void OpenPagesManager::reloadPages(const QString &nameSpace)
227 {
228     TRACE_OBJ
229     closeOrReloadPages(nameSpace, true);
230     m_openPagesWidget->selectCurrentPage();
231 }
232 
closeOrReloadPages(const QString & nameSpace,bool tryReload)233 void OpenPagesManager::closeOrReloadPages(const QString &nameSpace, bool tryReload)
234 {
235     TRACE_OBJ
236     for (int i = m_model->rowCount() - 1; i >= 0; --i) {
237         HelpViewer *page = m_model->pageAt(i);
238         if (page->source().host() != nameSpace)
239             continue;
240         if (tryReload && HelpEngineWrapper::instance().findFile(page->source()).isValid())
241             page->reload();
242         else if (m_model->rowCount() == 1)
243             page->setSource(QUrl(QLatin1String("about:blank")));
244         else
245             removePage(i);
246     }
247 }
248 
pagesOpenForNamespace(const QString & nameSpace) const249 bool OpenPagesManager::pagesOpenForNamespace(const QString &nameSpace) const
250 {
251     TRACE_OBJ
252     for (int i = 0; i < m_model->rowCount(); ++i)
253         if (m_model->pageAt(i)->source().host() == nameSpace)
254             return true;
255     return false;
256 }
257 
setCurrentPage(const QModelIndex & index)258 void OpenPagesManager::setCurrentPage(const QModelIndex &index)
259 {
260     TRACE_OBJ
261     if (index.isValid())
262         setCurrentPage(index.row());
263 }
264 
setCurrentPage(int index)265 void OpenPagesManager::setCurrentPage(int index)
266 {
267     TRACE_OBJ
268     setCurrentPage(m_model->pageAt(index));
269 }
270 
setCurrentPage(HelpViewer * page)271 void OpenPagesManager::setCurrentPage(HelpViewer *page)
272 {
273     TRACE_OBJ
274     CentralWidget::instance()->setCurrentPage(page);
275     m_openPagesWidget->selectCurrentPage();
276 }
277 
removePage(int index)278 void OpenPagesManager::removePage(int index)
279 {
280     TRACE_OBJ
281     emit aboutToClosePage(index);
282 
283     CentralWidget::instance()->removePage(index);
284     m_model->removePage(index);
285     m_openPagesWidget->selectCurrentPage();
286 
287     emit pageClosed();
288 }
289 
290 
closePagesExcept(const QModelIndex & index)291 void OpenPagesManager::closePagesExcept(const QModelIndex &index)
292 {
293     TRACE_OBJ
294     if (!index.isValid())
295         return;
296 
297     int i = 0;
298     HelpViewer *viewer = m_model->pageAt(index.row());
299     while (m_model->rowCount() > 1) {
300         if (m_model->pageAt(i) != viewer)
301             removePage(i);
302         else
303             ++i;
304     }
305 }
306 
openPagesWidget() const307 QAbstractItemView *OpenPagesManager::openPagesWidget() const
308 {
309     TRACE_OBJ
310     return m_openPagesWidget;
311 }
312 
nextPage()313 void OpenPagesManager::nextPage()
314 {
315     TRACE_OBJ
316     nextOrPreviousPage(1);
317 }
318 
nextPageWithSwitcher()319 void OpenPagesManager::nextPageWithSwitcher()
320 {
321     TRACE_OBJ
322     if (!m_openPagesSwitcher->isVisible()) {
323         m_openPagesSwitcher->selectCurrentPage();
324         m_openPagesSwitcher->gotoNextPage();
325         showSwitcherOrSelectPage();
326     } else {
327         m_openPagesSwitcher->gotoNextPage();
328     }
329 }
330 
previousPage()331 void OpenPagesManager::previousPage()
332 {
333     TRACE_OBJ
334     nextOrPreviousPage(-1);
335 }
336 
previousPageWithSwitcher()337 void OpenPagesManager::previousPageWithSwitcher()
338 {
339     TRACE_OBJ
340     if (!m_openPagesSwitcher->isVisible()) {
341         m_openPagesSwitcher->selectCurrentPage();
342         m_openPagesSwitcher->gotoPreviousPage();
343         showSwitcherOrSelectPage();
344     } else {
345         m_openPagesSwitcher->gotoPreviousPage();
346     }
347 }
348 
nextOrPreviousPage(int offset)349 void OpenPagesManager::nextOrPreviousPage(int offset)
350 {
351     TRACE_OBJ
352     setCurrentPage((CentralWidget::instance()->currentIndex() + offset
353         + m_model->rowCount()) % m_model->rowCount());
354 }
355 
showSwitcherOrSelectPage() const356 void OpenPagesManager::showSwitcherOrSelectPage() const
357 {
358     TRACE_OBJ
359     if (QApplication::keyboardModifiers() != Qt::NoModifier) {
360         const int width = CentralWidget::instance()->width();
361         const int height = CentralWidget::instance()->height();
362         const QPoint p(CentralWidget::instance()->mapToGlobal(QPoint(0, 0)));
363         m_openPagesSwitcher->move((width - m_openPagesSwitcher->width()) / 2 + p.x(),
364             (height - m_openPagesSwitcher->height()) / 2 + p.y());
365         m_openPagesSwitcher->setVisible(true);
366     } else {
367         m_openPagesSwitcher->selectAndHide();
368     }
369 }
370 
371 QT_END_NAMESPACE
372