helpplugin.cpp 24.2 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
25

con's avatar
con committed
26
#include "helpplugin.h"
27

28
#include "bookmarkmanager.h"
29
#include "centralwidget.h"
con's avatar
con committed
30 31
#include "docsettingspage.h"
#include "filtersettingspage.h"
32
#include "generalsettingspage.h"
kh1's avatar
kh1 committed
33
#include "helpconstants.h"
34
#include "helpfindsupport.h"
con's avatar
con committed
35 36 37
#include "helpindexfilter.h"
#include "helpmode.h"
#include "helpviewer.h"
38
#include "localhelpmanager.h"
39 40
#include "openpagesmanager.h"
#include "openpagesmodel.h"
41
#include "qtwebkithelpviewer.h"
kh1's avatar
kh1 committed
42
#include "remotehelpfilter.h"
con's avatar
con committed
43
#include "searchwidget.h"
44
#include "searchtaskhandler.h"
45
#include "textbrowserhelpviewer.h"
con's avatar
con committed
46

47 48 49
#ifdef QTC_MAC_NATIVE_HELPVIEWER
#include "macwebkithelpviewer.h"
#endif
50 51 52
#ifdef QTC_WEBENGINE_HELPVIEWER
#include "webenginehelpviewer.h"
#endif
53

54 55 56 57
#include <bookmarkmanager.h>
#include <contentwindow.h>
#include <indexwindow.h>

58
#include <app/app_version.h>
59
#include <coreplugin/actionmanager/actionmanager.h>
60 61
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
62
#include <coreplugin/id.h>
63 64
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
65
#include <coreplugin/editormanager/ieditor.h>
66 67
#include <coreplugin/findplaceholder.h>
#include <coreplugin/icore.h>
hjk's avatar
hjk committed
68 69 70 71
#include <coreplugin/minisplitter.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/rightpane.h>
#include <coreplugin/sidebar.h>
kh1's avatar
kh1 committed
72
#include <extensionsystem/pluginmanager.h>
73
#include <coreplugin/find/findplugin.h>
74
#include <texteditor/texteditorconstants.h>
75
#include <utils/algorithm.h>
76
#include <utils/hostosinfo.h>
77
#include <utils/qtcassert.h>
78
#include <utils/styledbar.h>
79
#include <utils/theme/theme.h>
80
#include <utils/tooltip/tooltip.h>
81

82 83 84 85 86 87 88
#include <QDir>
#include <QFileInfo>
#include <QLibraryInfo>
#include <QTimer>
#include <QTranslator>
#include <qplugin.h>
#include <QRegExp>
kh1's avatar
kh1 committed
89

90 91 92 93 94 95
#include <QAction>
#include <QComboBox>
#include <QDesktopServices>
#include <QMenu>
#include <QStackedLayout>
#include <QSplitter>
kh1's avatar
kh1 committed
96

97
#include <QHelpEngine>
con's avatar
con committed
98

99 100
#include <functional>

con's avatar
con committed
101 102
using namespace Help::Internal;

103
static const char kExternalWindowStateKey[] = "Help/ExternalWindowState";
104
static const char kToolTipHelpContext[] = "Help.ToolTip";
105

106
using namespace Core;
107
using namespace Utils;
108

kh1's avatar
kh1 committed
109
HelpPlugin::HelpPlugin()
110
    : m_mode(0),
con's avatar
con committed
111
    m_centralWidget(0),
112
    m_rightPaneSideBarWidget(0),
113
    m_setupNeeded(true),
114
    m_helpManager(0),
115
    m_openPagesManager(0)
con's avatar
con committed
116 117 118 119 120
{
}

HelpPlugin::~HelpPlugin()
{
121 122
    delete m_openPagesManager;
    delete m_helpManager;
con's avatar
con committed
123 124
}

125
bool HelpPlugin::initialize(const QStringList &arguments, QString *error)
con's avatar
con committed
126
{
127 128
    Q_UNUSED(arguments)
    Q_UNUSED(error)
129
    Context modecontext(Constants::C_MODE_HELP);
con's avatar
con committed
130

131
    const QString &locale = ICore::userInterfaceLanguage();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
132 133
    if (!locale.isEmpty()) {
        QTranslator *qtr = new QTranslator(this);
134
        QTranslator *qhelptr = new QTranslator(this);
135
        const QString &creatorTrPath = ICore::resourcePath()
136
            + QLatin1String("/translations");
137 138 139 140 141 142 143
        const QString &qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
        const QString &trFile = QLatin1String("assistant_") + locale;
        const QString &helpTrFile = QLatin1String("qt_help_") + locale;
        if (qtr->load(trFile, qtTrPath) || qtr->load(trFile, creatorTrPath))
            qApp->installTranslator(qtr);
        if (qhelptr->load(helpTrFile, qtTrPath) || qhelptr->load(helpTrFile, creatorTrPath))
            qApp->installTranslator(qhelptr);
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
144 145
    }

146 147
    m_helpManager = new LocalHelpManager(this);
    m_openPagesManager = new OpenPagesManager(this);
148 149
    addAutoReleasedObject(m_docSettingsPage = new DocSettingsPage());
    addAutoReleasedObject(m_filterSettingsPage = new FilterSettingsPage());
150
    addAutoReleasedObject(new GeneralSettingsPage());
151
    addAutoReleasedObject(m_searchTaskHandler = new SearchTaskHandler);
152

153
    m_centralWidget = new CentralWidget(modecontext);
154 155 156 157 158
    connect(m_centralWidget, SIGNAL(sourceChanged(QUrl)), this,
        SLOT(updateSideBarSource(QUrl)));
    connect(m_centralWidget, &CentralWidget::closeButtonClicked,
            &OpenPagesManager::instance(), &OpenPagesManager::closeCurrentPage);

159 160
    connect(LocalHelpManager::instance(), &LocalHelpManager::returnOnCloseChanged,
            m_centralWidget, &CentralWidget::updateCloseButton);
161 162
    connect(HelpManager::instance(), SIGNAL(helpRequested(QUrl,Core::HelpManager::HelpViewerLocation)),
            this, SLOT(handleHelpRequest(QUrl,Core::HelpManager::HelpViewerLocation)));
163
    connect(m_searchTaskHandler, SIGNAL(search(QUrl)), this,
164
            SLOT(showLinkInHelpMode(QUrl)));
165

166 167
    connect(m_filterSettingsPage, SIGNAL(filtersChanged()), this,
        SLOT(setupHelpEngineIfNeeded()));
168
    connect(HelpManager::instance(), SIGNAL(documentationChanged()), this,
169
        SLOT(setupHelpEngineIfNeeded()));
170
    connect(HelpManager::instance(), SIGNAL(collectionFileChanged()), this,
171
        SLOT(setupHelpEngineIfNeeded()));
con's avatar
con committed
172

173 174 175 176 177 178 179
    connect(ToolTip::instance(), &ToolTip::shown, ICore::instance(), []() {
        ICore::addAdditionalContext(Context(kToolTipHelpContext), ICore::ContextPriority::High);
    });
    connect(ToolTip::instance(), &ToolTip::hidden,ICore::instance(), []() {
        ICore::removeAdditionalContext(Context(kToolTipHelpContext));
    });

180
    Command *cmd;
181
    QAction *action;
con's avatar
con committed
182

183
    // Add Contents, Index, and Context menu items
184
    action = new QAction(QIcon::fromTheme(QLatin1String("help-contents")),
185
        tr(Constants::SB_CONTENTS), this);
186
    cmd = ActionManager::registerAction(action, "Help.ContentsMenu");
187
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
188 189
    connect(action, SIGNAL(triggered()), this, SLOT(activateContents()));

190
    action = new QAction(tr(Constants::SB_INDEX), this);
191
    cmd = ActionManager::registerAction(action, "Help.IndexMenu");
192
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
193 194 195
    connect(action, SIGNAL(triggered()), this, SLOT(activateIndex()));

    action = new QAction(tr("Context Help"), this);
196 197
    cmd = ActionManager::registerAction(action, Help::Constants::CONTEXT_HELP,
                                        Context(kToolTipHelpContext, Core::Constants::C_GLOBAL));
198
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
con's avatar
con committed
199
    cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F1));
200
    connect(action, SIGNAL(triggered()), this, SLOT(showContextHelp()));
con's avatar
con committed
201

202
    action = new QAction(tr("Technical Support"), this);
203
    cmd = ActionManager::registerAction(action, "Help.TechSupport");
204
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
205 206
    connect(action, SIGNAL(triggered()), this, SLOT(slotOpenSupportPage()));

207
    action = new QAction(tr("Report Bug..."), this);
208
    cmd = ActionManager::registerAction(action, "Help.ReportBug");
209
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
210 211
    connect(action, SIGNAL(triggered()), this, SLOT(slotReportBug()));

212
    if (ActionContainer *windowMenu = ActionManager::actionContainer(Core::Constants::M_WINDOW)) {
213
        // reuse EditorManager constants to avoid a second pair of menu actions
214
        // Goto Previous In History Action
Jarek Kobus's avatar
Jarek Kobus committed
215
        action = new QAction(this);
216 217
        Command *ctrlTab = ActionManager::registerAction(action, Core::Constants::GOTOPREVINHISTORY,
            modecontext);
218 219 220 221
        windowMenu->addAction(ctrlTab, Core::Constants::G_WINDOW_NAVIGATE);
        connect(action, SIGNAL(triggered()), &OpenPagesManager::instance(),
            SLOT(gotoPreviousPage()));

222
        // Goto Next In History Action
Jarek Kobus's avatar
Jarek Kobus committed
223
        action = new QAction(this);
224 225
        Command *ctrlShiftTab = ActionManager::registerAction(action, Core::Constants::GOTONEXTINHISTORY,
            modecontext);
226 227 228 229 230
        windowMenu->addAction(ctrlShiftTab, Core::Constants::G_WINDOW_NAVIGATE);
        connect(action, SIGNAL(triggered()), &OpenPagesManager::instance(),
            SLOT(gotoNextPage()));
    }

231
    auto helpIndexFilter = new HelpIndexFilter();
232
    addAutoReleasedObject(helpIndexFilter);
233 234 235 236
    connect(helpIndexFilter, &HelpIndexFilter::linkActivated,
            this, &HelpPlugin::showLinkInHelpMode);
    connect(helpIndexFilter, &HelpIndexFilter::linksActivated,
            this, &HelpPlugin::showLinksInHelpMode);
237

kh1's avatar
kh1 committed
238 239 240
    RemoteHelpFilter *remoteHelpFilter = new RemoteHelpFilter();
    addAutoReleasedObject(remoteHelpFilter);
    connect(remoteHelpFilter, SIGNAL(linkActivated(QUrl)), this,
241
        SLOT(showLinkInHelpMode(QUrl)));
kh1's avatar
kh1 committed
242

243
    QDesktopServices::setUrlHandler(QLatin1String("qthelp"), HelpManager::instance(), "handleHelpRequest");
244 245
    connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*,Core::IMode*)),
            this, SLOT(modeChanged(Core::IMode*,Core::IMode*)));
246

247
    m_mode = new HelpMode;
248
    m_mode->setWidget(m_centralWidget);
249
    addAutoReleasedObject(m_mode);
250

con's avatar
con committed
251 252 253
    return true;
}

254
void HelpPlugin::extensionsInitialized()
255
{
256 257
    QStringList filesToRegister;
    // we might need to register creators inbuild help
258
    filesToRegister.append(ICore::documentationPath() + QLatin1String("/qtcreator.qch"));
259
    HelpManager::registerDocumentation(filesToRegister);
260 261
}

262
ExtensionSystem::IPlugin::ShutdownFlag HelpPlugin::aboutToShutdown()
263
{
264 265 266 267 268 269
    if (m_externalWindow)
        delete m_externalWindow.data();
    if (m_centralWidget)
        delete m_centralWidget;
    if (m_rightPaneSideBarWidget)
        delete m_rightPaneSideBarWidget;
270
    return SynchronousShutdown;
271 272
}

kh1's avatar
kh1 committed
273 274
void HelpPlugin::resetFilter()
{
kh1's avatar
kh1 committed
275 276
    const QString &filterInternal = QString::fromLatin1("Qt Creator %1.%2.%3")
        .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR).arg(IDE_VERSION_RELEASE);
277
    QRegExp filterRegExp(QLatin1String("Qt Creator \\d*\\.\\d*\\.\\d*"));
kh1's avatar
kh1 committed
278 279 280 281 282 283 284 285

    QHelpEngineCore *engine = &LocalHelpManager::helpEngine();
    const QStringList &filters = engine->customFilters();
    foreach (const QString &filter, filters) {
        if (filterRegExp.exactMatch(filter) && filter != filterInternal)
            engine->removeCustomFilter(filter);
    }

286 287 288 289
    // we added a filter at some point, remove previously added filter
    if (engine->customValue(Help::Constants::WeAddedFilterKey).toInt() == 1) {
        const QString &filter =
            engine->customValue(Help::Constants::PreviousFilterNameKey).toString();
290
        if (!filter.isEmpty())
kh1's avatar
kh1 committed
291
            engine->removeCustomFilter(filter);
kh1's avatar
kh1 committed
292 293 294 295
    }

    // potentially remove a filter with new name
    const QString filterName = tr("Unfiltered");
kh1's avatar
kh1 committed
296 297
    engine->removeCustomFilter(filterName);
    engine->addCustomFilter(filterName, QStringList());
298 299
    engine->setCustomValue(Help::Constants::WeAddedFilterKey, 1);
    engine->setCustomValue(Help::Constants::PreviousFilterNameKey, filterName);
kh1's avatar
kh1 committed
300 301
    engine->setCurrentFilter(filterName);

302 303 304
    LocalHelpManager::updateFilterModel();
    connect(engine, &QHelpEngineCore::setupFinished,
            LocalHelpManager::instance(), &LocalHelpManager::updateFilterModel);
305 306
}

307
void HelpPlugin::saveExternalWindowSettings()
con's avatar
con committed
308
{
309
    if (!m_externalWindow)
310
        return;
311
    m_externalWindowState = m_externalWindow->geometry();
312
    QSettings *settings = ICore::settings();
313 314 315
    settings->setValue(QLatin1String(kExternalWindowStateKey),
                       qVariantFromValue(m_externalWindowState));
}
con's avatar
con committed
316

317 318 319
HelpWidget *HelpPlugin::createHelpWidget(const Context &context, HelpWidget::WidgetStyle style)
{
    HelpWidget *widget = new HelpWidget(context, style);
320

321
    connect(widget->currentViewer(), SIGNAL(loadFinished()),
322
            this, SLOT(highlightSearchTermsInContextHelp()));
323
    connect(widget, SIGNAL(openHelpMode(QUrl)),
324
            this, SLOT(showLinkInHelpMode(QUrl)));
325
    connect(widget, SIGNAL(closeButtonClicked()),
326
            this, SLOT(slotHideRightPane()));
327 328
    connect(widget, SIGNAL(aboutToClose()),
            this, SLOT(saveExternalWindowSettings()));
329

kh1's avatar
kh1 committed
330 331
    // force setup, as we might have never switched to full help mode
    // thus the help engine might still run without collection file setup
332
    LocalHelpManager::setupGuiHelpEngine();
333 334 335 336 337 338 339 340

    return widget;
}

void HelpPlugin::createRightPaneContextViewer()
{
    if (m_rightPaneSideBarWidget)
        return;
341
    m_rightPaneSideBarWidget = createHelpWidget(Context(Constants::C_HELP_SIDEBAR),
342 343 344 345 346 347 348
                                                HelpWidget::SideBarWidget);
}

HelpViewer *HelpPlugin::externalHelpViewer()
{
    if (m_externalWindow)
        return m_externalWindow->currentViewer();
349
    doSetupIfNeeded();
350
    m_externalWindow = createHelpWidget(Context(Constants::C_HELP_EXTERNAL),
351 352
                                        HelpWidget::ExternalWindow);
    if (m_externalWindowState.isNull()) {
353
        QSettings *settings = ICore::settings();
354 355
        m_externalWindowState = settings->value(QLatin1String(kExternalWindowStateKey)).toRect();
    }
356 357 358
    if (m_externalWindowState.isNull())
        m_externalWindow->resize(650, 700);
    else
359 360
        m_externalWindow->setGeometry(m_externalWindowState);
    m_externalWindow->show();
361
    m_externalWindow->setFocus();
362
    return m_externalWindow->currentViewer();
con's avatar
con committed
363 364
}

365 366
HelpViewer *HelpPlugin::createHelpViewer(qreal zoom)
{
367 368
    // check for backends
    typedef std::function<HelpViewer *()> ViewerFactory;
369 370 371 372 373 374 375 376 377 378
    typedef QPair<QByteArray, ViewerFactory>  ViewerFactoryItem; // id -> factory
    QVector<ViewerFactoryItem> factories;
#ifndef QT_NO_WEBKIT
    factories.append(qMakePair(QByteArray("qtwebkit"), []() { return new QtWebKitHelpViewer(); }));
#endif
#ifdef QTC_WEBENGINE_HELPVIEWER
    factories.append(qMakePair(QByteArray("qtwebengine"), []() { return new WebEngineHelpViewer(); }));
#endif
    factories.append(qMakePair(QByteArray("textbrowser"), []() { return new TextBrowserHelpViewer(); }));

379
#ifdef QTC_MAC_NATIVE_HELPVIEWER
380 381 382 383 384
    // default setting
#ifdef QTC_MAC_NATIVE_HELPVIEWER_DEFAULT
     factories.prepend(qMakePair(QByteArray("native"), []() { return new MacWebKitHelpViewer(); }));
#else
     factories.append(qMakePair(QByteArray("native"), []() { return new MacWebKitHelpViewer(); }));
385
#endif
386
#endif
387

388
    HelpViewer *viewer = nullptr;
389

390
    // check requested backend
391
    const QByteArray backend = qgetenv("QTC_HELPVIEWER_BACKEND");
392
    if (!backend.isEmpty()) {
393 394 395 396 397
        const int pos = Utils::indexOf(factories, [backend](const ViewerFactoryItem &item) {
            return backend == item.first;
        });
        if (pos == -1) {
            qWarning("Help viewer backend \"%s\" not found, using default.", backend.constData());
398
        } else {
399
            viewer  = factories.at(pos).second();
400
        }
401
    }
402 403 404 405

    if (!viewer)
        viewer = factories.first().second();
    QTC_ASSERT(viewer, return nullptr);
406 407

    // initialize font
408 409 410
    viewer->setViewerFont(LocalHelpManager::fallbackFont());
    connect(LocalHelpManager::instance(), &LocalHelpManager::fallbackFontChanged,
            viewer, &HelpViewer::setViewerFont);
411

412 413 414
    // initialize zoom
    viewer->setScale(zoom);

415 416 417 418 419
    // add find support
    Aggregation::Aggregate *agg = new Aggregation::Aggregate();
    agg->add(viewer);
    agg->add(new HelpViewerFindSupport(viewer));

420
    return viewer;
421 422
}

hjk's avatar
hjk committed
423 424
void HelpPlugin::activateHelpMode()
{
425
    ModeManager::activateMode(Id(Constants::ID_MODE_HELP));
hjk's avatar
hjk committed
426 427
}

428
void HelpPlugin::showLinkInHelpMode(const QUrl &source)
con's avatar
con committed
429
{
hjk's avatar
hjk committed
430
    activateHelpMode();
431
    ICore::raiseWindow(m_mode->widget());
con's avatar
con committed
432 433 434 435
    m_centralWidget->setSource(source);
    m_centralWidget->setFocus();
}

436 437 438
void HelpPlugin::showLinksInHelpMode(const QMap<QString, QUrl> &links, const QString &key)
{
    activateHelpMode();
439
    ICore::raiseWindow(m_mode->widget());
440 441 442
    m_centralWidget->showTopicChooser(links, key);
}

con's avatar
con committed
443 444
void HelpPlugin::slotHideRightPane()
{
445
    RightPaneWidget::instance()->setShown(false);
con's avatar
con committed
446 447
}

448
void HelpPlugin::modeChanged(IMode *mode, IMode *old)
con's avatar
con committed
449
{
450
    Q_UNUSED(old)
451
    if (mode == m_mode) {
452
        qApp->setOverrideCursor(Qt::WaitCursor);
kh1's avatar
kh1 committed
453
        doSetupIfNeeded();
con's avatar
con committed
454 455 456 457
        qApp->restoreOverrideCursor();
    }
}

458 459
void HelpPlugin::updateSideBarSource()
{
460
    if (HelpViewer *viewer = m_centralWidget->currentViewer()) {
461 462 463 464
        const QUrl &url = viewer->source();
        if (url.isValid())
            updateSideBarSource(url);
    }
465 466 467 468
}

void HelpPlugin::updateSideBarSource(const QUrl &newUrl)
{
469 470 471 472 473 474 475
    if (m_rightPaneSideBarWidget) {
        // This is called when setSource on the central widget is called.
        // Avoid nested setSource calls (even of different help viewers) by scheduling the
        // sidebar viewer update on the event loop (QTCREATORBUG-12742)
        QMetaObject::invokeMethod(m_rightPaneSideBarWidget->currentViewer(), "setSource",
                                  Qt::QueuedConnection, Q_ARG(QUrl, newUrl));
    }
476 477
}

478 479
void HelpPlugin::setupHelpEngineIfNeeded()
{
480
    LocalHelpManager::setEngineNeedsUpdate();
hjk's avatar
hjk committed
481
    if (ModeManager::currentMode() == m_mode->id()
482
            || LocalHelpManager::contextHelpOption() == HelpManager::ExternalHelpAlways)
483
        LocalHelpManager::setupGuiHelpEngine();
484 485
}

486
bool HelpPlugin::canShowHelpSideBySide() const
con's avatar
con committed
487
{
488
    RightPanePlaceHolder *placeHolder = RightPanePlaceHolder::current();
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
    if (!placeHolder)
        return false;
    if (placeHolder->isVisible())
        return true;

    IEditor *editor = EditorManager::currentEditor();
    if (!editor)
        return true;
    QTC_ASSERT(editor->widget(), return true);
    if (!editor->widget()->isVisible())
        return true;
    if (editor->widget()->width() < 800)
        return false;
    return true;
}

505
HelpViewer *HelpPlugin::viewerForHelpViewerLocation(HelpManager::HelpViewerLocation location)
506
{
507 508 509 510
    HelpManager::HelpViewerLocation actualLocation = location;
    if (location == HelpManager::SideBySideIfPossible)
        actualLocation = canShowHelpSideBySide() ? HelpManager::SideBySideAlways
                                                 : HelpManager::HelpModeAlways;
511

512
    if (actualLocation == HelpManager::ExternalHelpAlways)
513
        return externalHelpViewer();
514

515
    if (actualLocation == HelpManager::SideBySideAlways) {
516
        createRightPaneContextViewer();
517
        RightPaneWidget::instance()->setWidget(m_rightPaneSideBarWidget);
518
        RightPaneWidget::instance()->setShown(true);
519
        return m_rightPaneSideBarWidget->currentViewer();
kh's avatar
kh committed
520
    }
521

522
    QTC_CHECK(actualLocation == HelpManager::HelpModeAlways);
523

524
    activateHelpMode(); // should trigger an createPage...
525
    HelpViewer *viewer = m_centralWidget->currentViewer();
526 527
    if (!viewer)
        viewer = OpenPagesManager::instance().createPage();
528 529 530
    return viewer;
}

531 532
HelpViewer *HelpPlugin::viewerForContextHelp()
{
533
    return viewerForHelpViewerLocation(LocalHelpManager::contextHelpOption());
534 535
}

536 537 538 539 540 541
static QUrl findBestLink(const QMap<QString, QUrl> &links, QString *highlightId)
{
    if (highlightId)
        highlightId->clear();
    if (links.isEmpty())
        return QUrl();
542
    QUrl source = links.constBegin().value();
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
    // workaround to show the latest Qt version
    int version = 0;
    QRegExp exp(QLatin1String("(\\d+)"));
    foreach (const QUrl &link, links) {
        const QString &authority = link.authority();
        if (authority.startsWith(QLatin1String("com.trolltech."))
                || authority.startsWith(QLatin1String("org.qt-project."))) {
            if (exp.indexIn(authority) >= 0) {
                const int tmpVersion = exp.cap(1).toInt();
                if (tmpVersion > version) {
                    source = link;
                    version = tmpVersion;
                    if (highlightId)
                        *highlightId = source.fragment();
                }
            }
        }
    }
    return source;
}

564
void HelpPlugin::showContextHelp()
565 566
{
    // Find out what to show
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
    QString contextHelpId = Utils::ToolTip::contextHelpId();
    IContext *context = ICore::currentContextObject();
    if (contextHelpId.isEmpty() && context)
        contextHelpId = context->contextHelpId();

    // get the viewer after getting the help id,
    // because a new window might be opened and therefore focus be moved
    HelpViewer *viewer = viewerForContextHelp();
    QTC_ASSERT(viewer, return);

    QMap<QString, QUrl> links = HelpManager::linksForIdentifier(contextHelpId);
    // Maybe the id is already an URL
    if (links.isEmpty() && LocalHelpManager::isValidUrl(contextHelpId))
        links.insert(contextHelpId, contextHelpId);

    QUrl source = findBestLink(links, &m_contextHelpHighlightId);
    if (!source.isValid()) {
        // No link found or no context object
        viewer->setSource(QUrl(Help::Constants::AboutBlank));
        viewer->setHtml(tr("<html><head><title>No Documentation</title>"
            "</head><body><br/><center>"
            "<font color=\"%1\"><b>%2</b></font><br/>"
            "<font color=\"%3\">No documentation available.</font>"
            "</center></body></html>")
            .arg(creatorTheme()->color(Theme::TextColorNormal).name())
            .arg(contextHelpId)
            .arg(creatorTheme()->color(Theme::TextColorNormal).name()));
    } else {
        viewer->setFocus();
596 597
        viewer->stop();
        viewer->setSource(source); // triggers loadFinished which triggers id highlighting
598
        ICore::raiseWindow(viewer);
con's avatar
con committed
599 600 601 602 603
    }
}

void HelpPlugin::activateIndex()
{
hjk's avatar
hjk committed
604
    activateHelpMode();
605
    m_centralWidget->activateSideBarItem(QLatin1String(Constants::HELP_INDEX));
con's avatar
con committed
606 607 608 609
}

void HelpPlugin::activateContents()
{
hjk's avatar
hjk committed
610
    activateHelpMode();
611
    m_centralWidget->activateSideBarItem(QLatin1String(Constants::HELP_CONTENTS));
kh1's avatar
kh1 committed
612 613
}

614
void HelpPlugin::highlightSearchTermsInContextHelp()
615
{
616 617
    if (m_contextHelpHighlightId.isEmpty())
        return;
618
    HelpViewer *viewer = viewerForContextHelp();
619 620 621
    QTC_ASSERT(viewer, return);
    viewer->highlightId(m_contextHelpHighlightId);
    m_contextHelpHighlightId.clear();
622 623
}

624
void HelpPlugin::handleHelpRequest(const QUrl &url, HelpManager::HelpViewerLocation location)
con's avatar
con committed
625
{
kh1's avatar
kh1 committed
626
    if (HelpViewer::launchWithExternalApp(url))
627 628
        return;

629
    QString address = url.toString();
630
    if (!HelpManager::findFile(url).isValid()) {
631 632
        if (address.startsWith(QLatin1String("qthelp://org.qt-project."))
            || address.startsWith(QLatin1String("qthelp://com.nokia."))
hjk's avatar
hjk committed
633
            || address.startsWith(QLatin1String("qthelp://com.trolltech."))) {
kh1's avatar
kh1 committed
634
                // local help not installed, resort to external web help
Sergio Ahumada's avatar
Sergio Ahumada committed
635
                QString urlPrefix = QLatin1String("http://doc.qt.io/");
636
                if (url.authority() == QLatin1String("org.qt-project.qtcreator"))
637
                    urlPrefix.append(QString::fromLatin1("qtcreator"));
638
                else
kh1's avatar
kh1 committed
639 640
                    urlPrefix.append(QLatin1String("latest"));
            address = urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/')));
641
        }
642 643 644
    }

    const QUrl newUrl(address);
645 646 647
    HelpViewer *viewer = viewerForHelpViewerLocation(location);
    QTC_ASSERT(viewer, return);
    viewer->setSource(newUrl);
648
    ICore::raiseWindow(viewer);
649
}
con's avatar
con committed
650

651 652
void HelpPlugin::slotOpenSupportPage()
{
653
    showLinkInHelpMode(QUrl(QLatin1String("qthelp://org.qt-project.qtcreator/doc/technical-support.html")));
654 655
}

656 657
void HelpPlugin::slotReportBug()
{
Eike Ziller's avatar
Eike Ziller committed
658
    QDesktopServices::openUrl(QUrl(QLatin1String("https://bugreports.qt.io")));
659 660
}

kh1's avatar
kh1 committed
661
void HelpPlugin::doSetupIfNeeded()
662
{
663
    LocalHelpManager::setupGuiHelpEngine();
664
    if (m_setupNeeded) {
665
        resetFilter();
666
        m_setupNeeded = false;
667
        OpenPagesManager::instance().setupInitialPages();
668
        LocalHelpManager::bookmarkManager().setupBookmarkModels();
669 670
    }
}