helpplugin.cpp 39.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
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
12
13
14
** 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
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29

con's avatar
con committed
30
#include "helpplugin.h"
31
32

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

50
51
52
53
#ifdef QTC_MAC_NATIVE_HELPVIEWER
#include "macwebkithelpviewer.h"
#endif

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>
kh1's avatar
kh1 committed
68
#include <coreplugin/helpmanager.h>
hjk's avatar
hjk committed
69
70
71
72
#include <coreplugin/minisplitter.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/rightpane.h>
#include <coreplugin/sidebar.h>
kh1's avatar
kh1 committed
73
#include <extensionsystem/pluginmanager.h>
74
#include <coreplugin/find/findplugin.h>
75
#include <texteditor/texteditorconstants.h>
76
#include <utils/hostosinfo.h>
77
#include <utils/qtcassert.h>
78
79
#include <utils/styledbar.h>

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

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

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

using namespace Help::Internal;

99
100
101
const char SB_INDEX[] = QT_TRANSLATE_NOOP("Help::Internal::HelpPlugin", "Index");
const char SB_CONTENTS[] = QT_TRANSLATE_NOOP("Help::Internal::HelpPlugin", "Contents");
const char SB_BOOKMARKS[] = QT_TRANSLATE_NOOP("Help::Internal::HelpPlugin", "Bookmarks");
102

103
const char SB_OPENPAGES[] = "OpenPages";
104

105
106
static const char kExternalWindowStateKey[] = "Help/ExternalWindowState";

107
#define IMAGEPATH ":/help/images/"
108

109
110
111
112
113
114
115
116
using namespace Core;

static QToolButton *toolButton(QAction *action)
{
    QToolButton *button = new QToolButton;
    button->setDefaultAction(action);
    button->setPopupMode(QToolButton::DelayedPopup);
    return button;
117
118
}

kh1's avatar
kh1 committed
119
HelpPlugin::HelpPlugin()
120
    : m_mode(0),
con's avatar
con committed
121
    m_centralWidget(0),
122
    m_rightPaneSideBarWidget(0),
con's avatar
con committed
123
124
125
126
    m_contentItem(0),
    m_indexItem(0),
    m_searchItem(0),
    m_bookmarkItem(0),
127
    m_sideBar(0),
128
    m_firstModeChange(true),
129
130
    m_helpManager(0),
    m_openPagesManager(0),
131
    m_backMenu(0),
132
133
    m_nextMenu(0),
    m_isSidebarVisible(true)
con's avatar
con committed
134
135
136
137
138
{
}

HelpPlugin::~HelpPlugin()
{
139
140
    delete m_centralWidget;
    delete m_openPagesManager;
141
    delete m_rightPaneSideBarWidget;
142
143

    delete m_helpManager;
con's avatar
con committed
144
145
}

146
bool HelpPlugin::initialize(const QStringList &arguments, QString *error)
con's avatar
con committed
147
{
148
149
    Q_UNUSED(arguments)
    Q_UNUSED(error)
150
151
    Context globalcontext(Core::Constants::C_GLOBAL);
    Context modecontext(Constants::C_MODE_HELP);
con's avatar
con committed
152

153
    const QString &locale = ICore::userInterfaceLanguage();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
154
155
    if (!locale.isEmpty()) {
        QTranslator *qtr = new QTranslator(this);
156
        QTranslator *qhelptr = new QTranslator(this);
157
        const QString &creatorTrPath = ICore::resourcePath()
158
            + QLatin1String("/translations");
159
160
161
162
163
164
165
        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
166
167
    }

168
169
    m_helpManager = new LocalHelpManager(this);
    m_openPagesManager = new OpenPagesManager(this);
170
171
    addAutoReleasedObject(m_docSettingsPage = new DocSettingsPage());
    addAutoReleasedObject(m_filterSettingsPage = new FilterSettingsPage());
172
    addAutoReleasedObject(m_generalSettingsPage = new GeneralSettingsPage());
173
    addAutoReleasedObject(m_searchTaskHandler = new SearchTaskHandler);
174

175
176
    connect(m_generalSettingsPage, SIGNAL(fontChanged()), this,
        SLOT(fontChanged()));
177
178
    connect(m_generalSettingsPage, SIGNAL(returnOnCloseChanged()), this,
        SLOT(updateCloseButton()));
179
    connect(HelpManager::instance(), SIGNAL(helpRequested(QUrl)), this,
kh1's avatar
kh1 committed
180
        SLOT(handleHelpRequest(QUrl)));
181
182
    connect(m_searchTaskHandler, SIGNAL(search(QUrl)), this,
            SLOT(switchToHelpMode(QUrl)));
183

184
185
    connect(m_filterSettingsPage, SIGNAL(filtersChanged()), this,
        SLOT(setupHelpEngineIfNeeded()));
186
    connect(HelpManager::instance(), SIGNAL(documentationChanged()), this,
187
        SLOT(setupHelpEngineIfNeeded()));
188
    connect(HelpManager::instance(), SIGNAL(collectionFileChanged()), this,
189
        SLOT(setupHelpEngineIfNeeded()));
190
    connect(HelpManager::instance(), SIGNAL(setupFinished()), this,
191
            SLOT(unregisterOldQtCreatorDocumentation()));
con's avatar
con committed
192

193
    m_splitter = new MiniSplitter;
194
    m_centralWidget = new Help::Internal::CentralWidget();
195
196
    connect(m_centralWidget, SIGNAL(sourceChanged(QUrl)), this,
        SLOT(updateSideBarSource(QUrl)));
con's avatar
con committed
197
    // Add Home, Previous and Next actions (used in the toolbar)
198
199
    QAction *action = new QAction(QIcon(QLatin1String(IMAGEPATH "home.png")),
        tr("Home"), this);
200
    ActionManager::registerAction(action, "Help.Home", globalcontext);
201
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(home()));
con's avatar
con committed
202

203
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "previous.png")),
204
        tr("Previous Page"), this);
205
    Command *cmd = ActionManager::registerAction(action, "Help.Previous", modecontext);
206
    cmd->setDefaultKeySequence(QKeySequence::Back);
207
208
209
210
    action->setEnabled(m_centralWidget->isBackwardAvailable());
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(backward()));
    connect(m_centralWidget, SIGNAL(backwardAvailable(bool)), action,
        SLOT(setEnabled(bool)));
con's avatar
con committed
211

212
213
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "next.png")), tr("Next Page"), this);
    cmd = ActionManager::registerAction(action, "Help.Next", modecontext);
214
    cmd->setDefaultKeySequence(QKeySequence::Forward);
215
216
217
218
    action->setEnabled(m_centralWidget->isForwardAvailable());
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(forward()));
    connect(m_centralWidget, SIGNAL(forwardAvailable(bool)), action,
        SLOT(setEnabled(bool)));
con's avatar
con committed
219

220
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "bookmark.png")),
con's avatar
con committed
221
        tr("Add Bookmark"), this);
222
223
    cmd = ActionManager::registerAction(action, "Help.AddBookmark", modecontext);
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+M") : tr("Ctrl+M")));
224
    connect(action, SIGNAL(triggered()), this, SLOT(addBookmark()));
con's avatar
con committed
225

226
    // Add Contents, Index, and Context menu items
227
228
    action = new QAction(QIcon::fromTheme(QLatin1String("help-contents")),
        tr(SB_CONTENTS), this);
229
230
    cmd = ActionManager::registerAction(action, "Help.Contents", globalcontext);
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
231
232
    connect(action, SIGNAL(triggered()), this, SLOT(activateContents()));

233
    action = new QAction(tr(SB_INDEX), this);
234
235
    cmd = ActionManager::registerAction(action, "Help.Index", globalcontext);
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
236
237
238
    connect(action, SIGNAL(triggered()), this, SLOT(activateIndex()));

    action = new QAction(tr("Context Help"), this);
239
    cmd = ActionManager::registerAction(action, Help::Constants::CONTEXT_HELP, globalcontext);
240
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
con's avatar
con committed
241
    cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F1));
242
    connect(action, SIGNAL(triggered()), this, SLOT(activateContext()));
con's avatar
con committed
243

244
    action = new QAction(tr("Technical Support"), this);
245
    cmd = ActionManager::registerAction(action, "Help.TechSupport", globalcontext);
246
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
247
248
    connect(action, SIGNAL(triggered()), this, SLOT(slotOpenSupportPage()));

249
    action = new QAction(tr("Report Bug..."), this);
250
    cmd = ActionManager::registerAction(action, "Help.ReportBug", globalcontext);
251
    ActionManager::actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_SUPPORT);
252
253
    connect(action, SIGNAL(triggered()), this, SLOT(slotReportBug()));

254
    action = new QAction(this);
255
    ActionManager::registerAction(action, Core::Constants::PRINT, modecontext);
256
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(print()));
con's avatar
con committed
257

258
    action = new QAction(this);
259
    cmd = ActionManager::registerAction(action, Core::Constants::COPY, modecontext);
260
261
262
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(copy()));
    action->setText(cmd->action()->text());
    action->setIcon(cmd->action()->icon());
263

264
    if (ActionContainer *advancedMenu = ActionManager::actionContainer(Core::Constants::M_EDIT_ADVANCED)) {
265
        // reuse TextEditor constants to avoid a second pair of menu actions
266
        action = new QAction(tr("Increase Font Size"), this);
267
        cmd = ActionManager::registerAction(action, TextEditor::Constants::INCREASE_FONT_SIZE, modecontext);
268
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(zoomIn()));
269
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
270

271
        action = new QAction(tr("Decrease Font Size"), this);
272
        cmd = ActionManager::registerAction(action, TextEditor::Constants::DECREASE_FONT_SIZE, modecontext);
273
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(zoomOut()));
274
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
275

276
        action = new QAction(tr("Reset Font Size"), this);
277
        cmd = ActionManager::registerAction(action, TextEditor::Constants::RESET_FONT_SIZE, modecontext);
278
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(resetZoom()));
279
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
280
281
    }

282
    if (ActionContainer *windowMenu = ActionManager::actionContainer(Core::Constants::M_WINDOW)) {
283
        // reuse EditorManager constants to avoid a second pair of menu actions
284
        // Goto Previous In History Action
Jarek Kobus's avatar
Jarek Kobus committed
285
        action = new QAction(this);
286
287
        Command *ctrlTab = ActionManager::registerAction(action, Core::Constants::GOTOPREVINHISTORY,
            modecontext);
288
289
290
291
        windowMenu->addAction(ctrlTab, Core::Constants::G_WINDOW_NAVIGATE);
        connect(action, SIGNAL(triggered()), &OpenPagesManager::instance(),
            SLOT(gotoPreviousPage()));

292
        // Goto Next In History Action
Jarek Kobus's avatar
Jarek Kobus committed
293
        action = new QAction(this);
294
295
        Command *ctrlShiftTab = ActionManager::registerAction(action, Core::Constants::GOTONEXTINHISTORY,
            modecontext);
296
297
298
299
300
        windowMenu->addAction(ctrlShiftTab, Core::Constants::G_WINDOW_NAVIGATE);
        connect(action, SIGNAL(triggered()), &OpenPagesManager::instance(),
            SLOT(gotoNextPage()));
    }

301
302
303
304
305
306
307
308
    QWidget *toolBarWidget = new QWidget;
    QHBoxLayout *toolBarLayout = new QHBoxLayout(toolBarWidget);
    toolBarLayout->setMargin(0);
    toolBarLayout->setSpacing(0);
    toolBarLayout->addWidget(m_externalHelpBar = createIconToolBar(true));
    toolBarLayout->addWidget(m_internalHelpBar = createIconToolBar(false));
    toolBarLayout->addWidget(createWidgetToolBar());

kh1's avatar
kh1 committed
309
310
311
    QWidget *mainWidget = new QWidget;
    m_splitter->addWidget(mainWidget);
    QVBoxLayout *mainWidgetLayout = new QVBoxLayout(mainWidget);
312
313
    mainWidgetLayout->setMargin(0);
    mainWidgetLayout->setSpacing(0);
314
    mainWidgetLayout->addWidget(toolBarWidget);
315
316
    mainWidgetLayout->addWidget(m_centralWidget);

317
318
    if (QLayout *layout = m_centralWidget->layout()) {
        layout->setSpacing(0);
319
        FindToolBarPlaceHolder *fth = new FindToolBarPlaceHolder(m_centralWidget);
320
        fth->setObjectName(QLatin1String("HelpFindToolBarPlaceHolder"));
321
        mainWidgetLayout->addWidget(fth);
322
323
    }

324
325
326
327
328
    HelpIndexFilter *helpIndexFilter = new HelpIndexFilter();
    addAutoReleasedObject(helpIndexFilter);
    connect(helpIndexFilter, SIGNAL(linkActivated(QUrl)), this,
        SLOT(switchToHelpMode(QUrl)));

kh1's avatar
kh1 committed
329
330
331
332
333
    RemoteHelpFilter *remoteHelpFilter = new RemoteHelpFilter();
    addAutoReleasedObject(remoteHelpFilter);
    connect(remoteHelpFilter, SIGNAL(linkActivated(QUrl)), this,
        SLOT(switchToHelpMode(QUrl)));

334
    QDesktopServices::setUrlHandler(QLatin1String("qthelp"), this, "handleHelpRequest");
335
336
    connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*,Core::IMode*)),
            this, SLOT(modeChanged(Core::IMode*,Core::IMode*)));
337

338
    m_mode = new HelpMode;
339
340
    m_mode->setWidget(m_splitter);
    m_internalHelpBar->setVisible(true);
341
    addAutoReleasedObject(m_mode);
342

con's avatar
con committed
343
344
345
    return true;
}

346
void HelpPlugin::extensionsInitialized()
347
{
348
349
    QStringList filesToRegister;
    // we might need to register creators inbuild help
350
    filesToRegister.append(ICore::documentationPath() + QLatin1String("/qtcreator.qch"));
351
    HelpManager::registerDocumentation(filesToRegister);
352
353
}

354
ExtensionSystem::IPlugin::ShutdownFlag HelpPlugin::aboutToShutdown()
355
{
356
    if (m_sideBar) {
357
        QSettings *settings = ICore::settings();
358
359
360
361
362
        m_sideBar->saveSettings(settings, QLatin1String("HelpSideBar"));
        // keep a boolean value to avoid to modify the sidebar class, at least some qml stuff
        // depends on the always visible property of the sidebar...
        settings->setValue(QLatin1String("HelpSideBar/") + QLatin1String("Visible"), m_isSidebarVisible);
    }
kh1's avatar
kh1 committed
363

364
    return SynchronousShutdown;
365
366
}

367
368
void HelpPlugin::unregisterOldQtCreatorDocumentation()
{
369
    const QString &nsInternal = QString::fromLatin1("org.qt-project.qtcreator.%1%2%3")
370
371
372
        .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR).arg(IDE_VERSION_RELEASE);

    QStringList documentationToUnregister;
373
    foreach (const QString &ns, HelpManager::registeredNamespaces()) {
374
        if (ns.startsWith(QLatin1String("org.qt-project.qtcreator."))
375
376
377
378
379
                && ns != nsInternal) {
            documentationToUnregister << ns;
        }
    }
    if (!documentationToUnregister.isEmpty())
380
        HelpManager::unregisterDocumentation(documentationToUnregister);
381
382
}

383
void HelpPlugin::setupUi()
384
{
385
    // side bar widgets and shortcuts
386
    Context modecontext(Constants::C_MODE_HELP);
387
388

    IndexWindow *indexWindow = new IndexWindow();
389
    indexWindow->setWindowTitle(tr(SB_INDEX));
390
    m_indexItem = new SideBarItem(indexWindow, QLatin1String(SB_INDEX));
391
392
393

    connect(indexWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));
Robert Loehning's avatar
Robert Loehning committed
394
395
    connect(indexWindow, SIGNAL(linksActivated(QMap<QString,QUrl>,QString)),
        m_centralWidget, SLOT(showTopicChooser(QMap<QString,QUrl>,QString)));
396

397
    QMap<QString, Command*> shortcutMap;
398
399
    QAction *action = new QAction(tr("Activate Index in Help mode"), m_splitter);
    Command *cmd = ActionManager::registerAction(action, "Help.IndexShortcut", modecontext);
400
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+I") : tr("Ctrl+Shift+I")));
401
    connect(action, SIGNAL(triggered()), this, SLOT(activateIndex()));
402
    shortcutMap.insert(QLatin1String(SB_INDEX), cmd);
403
404

    ContentWindow *contentWindow = new ContentWindow();
405
    contentWindow->setWindowTitle(tr(SB_CONTENTS));
406
    m_contentItem = new SideBarItem(contentWindow, QLatin1String(SB_CONTENTS));
407
408
    connect(contentWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));
409

410
411
    action = new QAction(tr("Activate Contents in Help mode"), m_splitter);
    cmd = ActionManager::registerAction(action, "Help.ContentsShortcut", modecontext);
412
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+Shift+C") : tr("Ctrl+Shift+C")));
413
    connect(action, SIGNAL(triggered()), this, SLOT(activateContents()));
414
    shortcutMap.insert(QLatin1String(SB_CONTENTS), cmd);
415

416
417
    m_searchItem = new SearchSideBarItem;
    connect(m_searchItem, SIGNAL(linkActivated(QUrl)), m_centralWidget,
418
        SLOT(setSourceFromSearch(QUrl)));
419

420
421
     action = new QAction(tr("Activate Search in Help mode"), m_splitter);
     cmd = ActionManager::registerAction(action, "Help.SearchShortcut", modecontext);
422
     cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+/") : tr("Ctrl+Shift+/")));
423
     connect(action, SIGNAL(triggered()), this, SLOT(activateSearch()));
424
     shortcutMap.insert(m_searchItem->id(), cmd);
425

kh1's avatar
kh1 committed
426
    BookmarkManager *manager = &LocalHelpManager::bookmarkManager();
427
    BookmarkWidget *bookmarkWidget = new BookmarkWidget(manager, 0, false);
428
    bookmarkWidget->setWindowTitle(tr(SB_BOOKMARKS));
429
    m_bookmarkItem = new SideBarItem(bookmarkWidget, QLatin1String(SB_BOOKMARKS));
430
431
    connect(bookmarkWidget, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));
432
433
    connect(bookmarkWidget, SIGNAL(createPage(QUrl,bool)), &OpenPagesManager::instance(),
            SLOT(createPage(QUrl,bool)));
434

435
436
     action = new QAction(tr("Activate Bookmarks in Help mode"), m_splitter);
     cmd = ActionManager::registerAction(action, "Help.BookmarkShortcut", modecontext);
437
     cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+B") : tr("Ctrl+Shift+B")));
438
     connect(action, SIGNAL(triggered()), this, SLOT(activateBookmarks()));
con's avatar
con committed
439
     shortcutMap.insert(QLatin1String(SB_BOOKMARKS), cmd);
440

441
442
    QWidget *openPagesWidget = OpenPagesManager::instance().openPagesWidget();
    openPagesWidget->setWindowTitle(tr("Open Pages"));
443
    m_openPagesItem = new SideBarItem(openPagesWidget, QLatin1String(SB_OPENPAGES));
444

445
446
    action = new QAction(tr("Activate Open Pages in Help mode"), m_splitter);
    cmd = ActionManager::registerAction(action, "Help.PagesShortcut", modecontext);
447
    cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Meta+O") : tr("Ctrl+Shift+O")));
448
    connect(action, SIGNAL(triggered()), this, SLOT(activateOpenPages()));
449
    shortcutMap.insert(QLatin1String(SB_OPENPAGES), cmd);
450

451
    QList<SideBarItem*> itemList;
452
453
    itemList << m_contentItem << m_indexItem << m_searchItem << m_bookmarkItem
        << m_openPagesItem;
454
    m_sideBar = new SideBar(itemList, QList<SideBarItem*>()
kh1's avatar
kh1 committed
455
        << m_contentItem << m_openPagesItem);
456
    m_sideBar->setCloseWhenEmpty(true);
457
    m_sideBar->setShortcutMap(shortcutMap);
458
    connect(m_sideBar, SIGNAL(sideBarClosed()), this, SLOT(onSideBarVisibilityChanged()));
459
460

    m_splitter->setOpaqueResize(false);
con's avatar
con committed
461
    m_splitter->insertWidget(0, m_sideBar);
462
463
    m_splitter->setStretchFactor(0, 0);
    m_splitter->setStretchFactor(1, 1);
464
    m_sideBar->readSettings(ICore::settings(), QLatin1String("HelpSideBar"));
465
    m_splitter->setSizes(QList<int>() << m_sideBar->size().width() << 300);
466
467
468
469
470

    m_toggleSideBarAction = new QAction(QIcon(QLatin1String(Core::Constants::ICON_TOGGLE_SIDEBAR)),
        tr("Show Sidebar"), this);
    m_toggleSideBarAction->setCheckable(true);
    connect(m_toggleSideBarAction, SIGNAL(triggered(bool)), this, SLOT(showHideSidebar()));
471
    cmd = ActionManager::registerAction(m_toggleSideBarAction, Core::Constants::TOGGLE_SIDEBAR, modecontext);
472
473
}

kh1's avatar
kh1 committed
474
475
void HelpPlugin::resetFilter()
{
kh1's avatar
kh1 committed
476
477
    const QString &filterInternal = QString::fromLatin1("Qt Creator %1.%2.%3")
        .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR).arg(IDE_VERSION_RELEASE);
478
    QRegExp filterRegExp(QLatin1String("Qt Creator \\d*\\.\\d*\\.\\d*"));
kh1's avatar
kh1 committed
479
480
481
482
483
484
485
486

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

487
488
489
490
    // 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();
491
        if (!filter.isEmpty())
kh1's avatar
kh1 committed
492
            engine->removeCustomFilter(filter);
kh1's avatar
kh1 committed
493
494
495
496
    }

    // potentially remove a filter with new name
    const QString filterName = tr("Unfiltered");
kh1's avatar
kh1 committed
497
498
    engine->removeCustomFilter(filterName);
    engine->addCustomFilter(filterName, QStringList());
499
500
    engine->setCustomValue(Help::Constants::WeAddedFilterKey, 1);
    engine->setCustomValue(Help::Constants::PreviousFilterNameKey, filterName);
kh1's avatar
kh1 committed
501
502
503
504
    engine->setCurrentFilter(filterName);

    updateFilterComboBox();
    connect(engine, SIGNAL(setupFinished()), this, SLOT(updateFilterComboBox()));
505
506
}

507
void HelpPlugin::saveExternalWindowSettings()
con's avatar
con committed
508
{
509
    if (!m_externalWindow)
510
        return;
511
512
513
514
515
    m_externalWindowState = m_externalWindow->geometry();
    QSettings *settings = Core::ICore::settings();
    settings->setValue(QLatin1String(kExternalWindowStateKey),
                       qVariantFromValue(m_externalWindowState));
}
con's avatar
con committed
516

517
518
519
HelpWidget *HelpPlugin::createHelpWidget(const Context &context, HelpWidget::WidgetStyle style)
{
    HelpWidget *widget = new HelpWidget(context, style);
520

521
    connect(widget->currentViewer(), SIGNAL(loadFinished()),
522
            this, SLOT(highlightSearchTermsInContextHelp()));
523
    connect(widget, SIGNAL(openHelpMode(QUrl)),
524
            this, SLOT(switchToHelpMode(QUrl)));
525
    connect(widget, SIGNAL(closeButtonClicked()),
526
            this, SLOT(slotHideRightPane()));
527
528
    connect(widget, SIGNAL(aboutToClose()),
            this, SLOT(saveExternalWindowSettings()));
529

kh1's avatar
kh1 committed
530
531
532
    // force setup, as we might have never switched to full help mode
    // thus the help engine might still run without collection file setup
    m_helpManager->setupGuiHelpEngine();
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558

    return widget;
}

void HelpPlugin::createRightPaneContextViewer()
{
    if (m_rightPaneSideBarWidget)
        return;
    m_rightPaneSideBarWidget = createHelpWidget(Core::Context(Constants::C_HELP_SIDEBAR),
                                                HelpWidget::SideBarWidget);
}

HelpViewer *HelpPlugin::externalHelpViewer()
{
    if (m_externalWindow)
        return m_externalWindow->currentViewer();
    m_externalWindow = createHelpWidget(Core::Context(Constants::C_HELP_EXTERNAL),
                                        HelpWidget::ExternalWindow);
    if (m_externalWindowState.isNull()) {
        QSettings *settings = Core::ICore::settings();
        m_externalWindowState = settings->value(QLatin1String(kExternalWindowStateKey)).toRect();
    }
    if (!m_externalWindowState.isNull())
        m_externalWindow->setGeometry(m_externalWindowState);
    m_externalWindow->show();
    return m_externalWindow->currentViewer();
con's avatar
con committed
559
560
}

561
562
HelpViewer *HelpPlugin::createHelpViewer(qreal zoom)
{
563
564
565
566
567
568
569
570
571
    HelpViewer *viewer = 0;
    const QString backend = QLatin1String(qgetenv("QTC_HELPVIEWER_BACKEND"));
    if (backend.compare(QLatin1String("native"), Qt::CaseInsensitive) == 0) {
#ifdef QTC_MAC_NATIVE_HELPVIEWER
        viewer = new MacWebKitHelpViewer(zoom);
#endif
    } else if (backend.compare(QLatin1String("textbrowser"), Qt::CaseInsensitive) == 0) {
        viewer = new TextBrowserHelpViewer(zoom);
    } else {
572
#ifndef QT_NO_WEBKIT
573
        viewer = new QtWebKitHelpViewer(zoom);
574
#else
575
        viewer = new TextBrowserHelpViewer(zoom);
576
#endif
577
578
579
580
581
582
    }

    // initialize font
    QVariant fontSetting = LocalHelpManager::engineFontSettings();
    if (fontSetting.isValid())
        viewer->setViewerFont(fontSetting.value<QFont>());
583
584
585
586
587
588

    // add find support
    Aggregation::Aggregate *agg = new Aggregation::Aggregate();
    agg->add(viewer);
    agg->add(new HelpViewerFindSupport(viewer));

589
    return viewer;
590
591
}

hjk's avatar
hjk committed
592
593
void HelpPlugin::activateHelpMode()
{
594
    ModeManager::activateMode(Id(Constants::ID_MODE_HELP));
hjk's avatar
hjk committed
595
596
}

con's avatar
con committed
597
598
void HelpPlugin::switchToHelpMode(const QUrl &source)
{
hjk's avatar
hjk committed
599
    activateHelpMode();
600
    Core::ICore::raiseWindow(m_mode->widget());
con's avatar
con committed
601
602
603
604
605
606
    m_centralWidget->setSource(source);
    m_centralWidget->setFocus();
}

void HelpPlugin::slotHideRightPane()
{
607
    RightPaneWidget::instance()->setShown(false);
con's avatar
con committed
608
609
}

610
611
612
void HelpPlugin::showHideSidebar()
{
    m_sideBar->setVisible(!m_sideBar->isVisible());
613
    onSideBarVisibilityChanged();
614
615
}

616
void HelpPlugin::modeChanged(IMode *mode, IMode *old)
con's avatar
con committed
617
{
618
    Q_UNUSED(old)
619
    if (mode == m_mode) {
620
        qApp->setOverrideCursor(Qt::WaitCursor);
kh1's avatar
kh1 committed
621
        doSetupIfNeeded();
con's avatar
con committed
622
623
624
625
        qApp->restoreOverrideCursor();
    }
}

626
627
void HelpPlugin::updateSideBarSource()
{
628
629
630
631
632
    if (HelpViewer *viewer = m_centralWidget->currentHelpViewer()) {
        const QUrl &url = viewer->source();
        if (url.isValid())
            updateSideBarSource(url);
    }
633
634
635
636
}

void HelpPlugin::updateSideBarSource(const QUrl &newUrl)
{
637
638
    if (m_rightPaneSideBarWidget)
        m_rightPaneSideBarWidget->currentViewer()->setSource(newUrl);
639
640
}

641
void HelpPlugin::updateCloseButton()
642
{
643
    const bool closeOnReturn = HelpManager::customValue(QLatin1String("ReturnOnClose"),
644
645
        false).toBool();
    m_closeButton->setEnabled((OpenPagesManager::instance().pageCount() > 1)
Orgad Shaneh's avatar
Orgad Shaneh committed
646
                              || closeOnReturn);
647
}
648

649
650
void HelpPlugin::fontChanged()
{
651
    if (!m_rightPaneSideBarWidget)
652
        createRightPaneContextViewer();
653

654
655
    QVariant fontSetting = LocalHelpManager::engineFontSettings();
    QFont font = fontSetting.isValid() ? fontSetting.value<QFont>()
656
                                       : m_rightPaneSideBarWidget->currentViewer()->viewerFont();
657

658
    m_rightPaneSideBarWidget->currentViewer()->setViewerFont(font);
659
660
661
662
    const int count = OpenPagesManager::instance().pageCount();
    for (int i = 0; i < count; ++i) {
        if (HelpViewer *viewer = CentralWidget::instance()->viewerAt(i))
            viewer->setViewerFont(font);
663
664
665
    }
}

666
667
668
669
670
671
672
673
674
675
676
QStackedLayout * layoutForWidget(QWidget *parent, QWidget *widget)
{
    QList<QStackedLayout*> list = parent->findChildren<QStackedLayout*>();
    foreach (QStackedLayout *layout, list) {
        const int index = layout->indexOf(widget);
        if (index >= 0)
            return layout;
    }
    return 0;
}

677
678
void HelpPlugin::setupHelpEngineIfNeeded()
{
kh1's avatar
kh1 committed
679
    m_helpManager->setEngineNeedsUpdate();
680
    if (ModeManager::currentMode() == m_mode
681
        || contextHelpOption() == Help::Constants::ExternalHelpAlways)
682
683
684
        m_helpManager->setupGuiHelpEngine();
}

685
HelpViewer *HelpPlugin::viewerForContextMode()
con's avatar
con committed
686
{
687
688
    bool showSideBySide = false;
    RightPanePlaceHolder *placeHolder = RightPanePlaceHolder::current();
689
690
    switch (contextHelpOption()) {
        case Help::Constants::SideBySideIfPossible: {
691
            // side by side if possible
hjk's avatar
hjk committed
692
            if (IEditor *editor = EditorManager::currentEditor()) {
693
                if (!placeHolder || !placeHolder->isVisible()) {
694
695
696
697
698
                    if (!editor->widget())
                        break;
                    if (!editor->widget()->isVisible())
                        break;
                    if (editor->widget()->width() < 800)
699
700
                        break;
                }
701
            }
702
        }   // fall through
703
        case Help::Constants::SideBySideAlways: {
704
705
706
            // side by side
            showSideBySide = true;
        }   break;
707
708
        case Help::Constants::ExternalHelpAlways:
            return externalHelpViewer();
709
710
        default: // help mode
            break;
711
712
    }

713
    if (placeHolder && showSideBySide) {
714
        createRightPaneContextViewer();
715
        RightPaneWidget::instance()->setWidget(m_rightPaneSideBarWidget);
716
        RightPaneWidget::instance()->setShown(true);
717
        return m_rightPaneSideBarWidget->currentViewer();
kh's avatar
kh committed
718
    }
719
720
721
722
723

    activateHelpMode(); // should trigger an createPage...
    HelpViewer *viewer = m_centralWidget->currentHelpViewer();
    if (!viewer)
        viewer = OpenPagesManager::instance().createPage();
724
725
726
    return viewer;
}

727
728
729
730
731
732
static QUrl findBestLink(const QMap<QString, QUrl> &links, QString *highlightId)
{
    if (highlightId)
        highlightId->clear();
    if (links.isEmpty())
        return QUrl();
733
    QUrl source = links.constBegin().value();
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
    // 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;
}

755
756
void HelpPlugin::activateContext()
{
757
    if (ModeManager::currentMode() == m_mode)
758
759
760
        return;

    // Find out what to show
761
    QMap<QString, QUrl> links;
762
    QString idFromContext;
hjk's avatar
hjk committed
763
    if (IContext *context = Core::ICore::currentContextObject()) {
764
765
        idFromContext = context->contextHelpId();
        links = HelpManager::linksForIdentifier(idFromContext);
766
        // Maybe the id is already an URL
767
768
        if (links.isEmpty() && LocalHelpManager::isValidUrl(idFromContext))
            links.insert(idFromContext, idFromContext);
769
770
    }

771
    if (HelpViewer* viewer = viewerForContextMode()) {
772
773
        QUrl source = findBestLink(links, &m_contextHelpHighlightId);
        if (!source.isValid()) {
kh's avatar
kh committed
774
            // No link found or no context object
775
            viewer->setSource(QUrl(Help::Constants::AboutBlank));
kh's avatar
kh committed
776
777
            viewer->setHtml(tr("<html><head><title>No Documentation</title>"
                "</head><body><br/><center><b>%1</b><br/>No documentation "
778
                "available.</center></body></html>").arg(idFromContext));
kh's avatar
kh committed
779
        } else {
780
781
782
            const QUrl &oldSource = viewer->source();
            if (source != oldSource) {
                viewer->stop();
783
                viewer->setSource(source); // triggers loadFinished which triggers id highlighting
784
            } else {
785
                viewer->scrollToAnchor(source.fragment());
786
            }
787
            viewer->setFocus();
788
            Core::ICore::raiseWindow(viewer);
con's avatar
con committed
789
790
791
792
793
794
        }
    }
}

void HelpPlugin::activateIndex()
{
hjk's avatar
hjk committed
795
    activateHelpMode();
con's avatar
con committed
796
797
798
799
800
    m_sideBar->activateItem(m_indexItem);
}

void HelpPlugin::activateContents()
{
hjk's avatar
hjk committed
801
    activateHelpMode();
con's avatar
con committed
802
803
804
805
806
    m_sideBar->activateItem(m_contentItem);
}

void HelpPlugin::activateSearch()
{
hjk's avatar
hjk committed
807
    activateHelpMode();
con's avatar
con committed
808
809
810
    m_sideBar->activateItem(m_searchItem);
}

811
812
813
814
815
816
void HelpPlugin::activateOpenPages()
{
    activateHelpMode();
    m_sideBar->activateItem(m_openPagesItem);
}

kh1's avatar
kh1 committed
817
818
819
820
821
822
void HelpPlugin::activateBookmarks()
{
    activateHelpMode();
    m_sideBar->activateItem(m_bookmarkItem);
}

823
Utils::StyledBar *HelpPlugin::createWidgetToolBar()
con's avatar
con committed
824
825
{
    m_filterComboBox = new QComboBox;
826
    m_filterComboBox->setMinimumContentsLength(15);
kh's avatar
kh committed
827
828
    connect(m_filterComboBox, SIGNAL(activated(QString)), this,
        SLOT(filterDocumentation(QString)));
829
830
831
832
    connect(m_filterComboBox, SIGNAL(currentIndexChanged(int)), this,
        SLOT(updateSideBarSource()));

    m_closeButton = new QToolButton();
833
    m_closeButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_CLOSE_DOCUMENT)));
Leena Miettinen's avatar
Leena Miettinen committed
834
    m_closeButton->setToolTip(tr("Close current page"));
835
836
837
838
    connect(m_closeButton, SIGNAL(clicked()), &OpenPagesManager::instance(),
        SLOT(closeCurrentPage()));
    connect(&OpenPagesManager::instance(), SIGNAL(pagesChanged()), this,
        SLOT(updateCloseButton()));
839
840
841
842
843
844
845
846
847
848
849
850

    Utils::StyledBar *toolBar = new Utils::StyledBar;

    QHBoxLayout *layout = new QHBoxLayout(toolBar);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(OpenPagesManager::instance().openPagesComboBox(), 10);
    layout->addSpacing(5);
    layout->addWidget(new QLabel(tr("Filtered by:")));
    layout->addWidget(m_filterComboBox);
    layout->addStretch();
    layout->addWidget(m_closeButton);
851
852
853
854

    return toolBar;
}

855
Utils::StyledBar *HelpPlugin::createIconToolBar(bool external)
856
{
857
858
    Utils::StyledBar *toolBar = new Utils::StyledBar;
    toolBar->setVisible(false);
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883

    QAction *home, *back, *next, *bookmark;
    if (external) {
        home = new QAction(QIcon(QLatin1String(IMAGEPATH "home.png")),
            tr("Home"), toolBar);
        connect(home, SIGNAL(triggered()), m_centralWidget, SLOT(home()));

        back = new QAction(QIcon(QLatin1String(IMAGEPATH "previous.png")),
            tr("Previous Page"), toolBar);
        back->setEnabled(m_centralWidget->isBackwardAvailable());
        connect(back, SIGNAL(triggered()), m_centralWidget, SLOT(backward()));
        connect(m_centralWidget, SIGNAL(backwardAvailable(bool)), back,
            SLOT(setEnabled(bool)));

        next = new QAction(QIcon(QLatin1String(IMAGEPATH "next.png")),
            tr("Next Page"), toolBar);
        next->setEnabled(m_centralWidget->isForwardAvailable());
        connect(next, SIGNAL(triggered()), m_centralWidget, SLOT(forward()));
        connect(m_centralWidget, SIGNAL(forwardAvailable(bool)), next,
            SLOT(setEnabled(bool)));

        bookmark = new QAction(QIcon(QLatin1String(IMAGEPATH "bookmark.png")),
            tr("Add Bookmark"), toolBar);
        connect(bookmark, SIGNAL(triggered()), this, SLOT(addBookmark()));
    } else {
884
885
886
887
        home = Core::ActionManager::command("Help.Home")->action();
        back = Core::ActionManager::<