helpplugin.cpp 40.2 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>
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/hostosinfo.h>
76
#include <utils/qtcassert.h>
77
78
#include <utils/styledbar.h>

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

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

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

using namespace Help::Internal;

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

102
static const char SB_OPENPAGES[] = "OpenPages";
103

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

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

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

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

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

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

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

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

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

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

174
175
    connect(m_generalSettingsPage, SIGNAL(fontChanged()), this,
        SLOT(fontChanged()));
176
177
    connect(m_generalSettingsPage, SIGNAL(returnOnCloseChanged()), this,
        SLOT(updateCloseButton()));
178
179
    connect(HelpManager::instance(), SIGNAL(helpRequested(QUrl,Core::HelpManager::HelpViewerLocation)),
            this, SLOT(handleHelpRequest(QUrl,Core::HelpManager::HelpViewerLocation)));
180
181
    connect(m_searchTaskHandler, SIGNAL(search(QUrl)), this,
            SLOT(switchToHelpMode(QUrl)));
182

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

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

202
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "previous.png")),
203
        tr("Previous Page"), this);
204
    Command *cmd = ActionManager::registerAction(action, "Help.Previous", modecontext);
205
    cmd->setDefaultKeySequence(QKeySequence::Back);
206
207
208
209
    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
210

211
212
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "next.png")), tr("Next Page"), this);
    cmd = ActionManager::registerAction(action, "Help.Next", modecontext);
213
    cmd->setDefaultKeySequence(QKeySequence::Forward);
214
215
216
217
    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
218

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

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

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

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

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

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

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

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

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

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

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

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

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

300
301
302
303
304
305
306
307
    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
308
309
310
    QWidget *mainWidget = new QWidget;
    m_splitter->addWidget(mainWidget);
    QVBoxLayout *mainWidgetLayout = new QVBoxLayout(mainWidget);
311
312
    mainWidgetLayout->setMargin(0);
    mainWidgetLayout->setSpacing(0);
313
    mainWidgetLayout->addWidget(toolBarWidget);
314
315
    mainWidgetLayout->addWidget(m_centralWidget);

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

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

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

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

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

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

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

353
ExtensionSystem::IPlugin::ShutdownFlag HelpPlugin::aboutToShutdown()
354
{
355
    if (m_sideBar) {
356
        QSettings *settings = ICore::settings();
357
358
359
360
361
        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
362

363
    return SynchronousShutdown;
364
365
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    m_toggleSideBarAction = new QAction(QIcon(QLatin1String(Core::Constants::ICON_TOGGLE_SIDEBAR)),
        tr("Show Sidebar"), this);
    m_toggleSideBarAction->setCheckable(true);
469
470
    m_toggleSideBarAction->setChecked(m_isSidebarVisible);
    connect(m_toggleSideBarAction, SIGNAL(triggered(bool)), this, SLOT(setSideBarVisible(bool)));
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

    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();
558
    m_externalWindow->setFocus();
559
    return m_externalWindow->currentViewer();
con's avatar
con committed
560
561
}

562
563
HelpViewer *HelpPlugin::createHelpViewer(qreal zoom)
{
564
565
566
567
568
569
570
571
572
    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 {
573
#ifndef QT_NO_WEBKIT
574
        viewer = new QtWebKitHelpViewer(zoom);
575
#else
576
        viewer = new TextBrowserHelpViewer(zoom);
577
#endif
578
579
580
581
582
583
    }

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

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

590
    return viewer;
591
592
}

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

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

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

611
void HelpPlugin::setSideBarVisible(bool visible)
612
{
613
614
615
    if (visible == m_sideBar->isVisible())
        return;
    m_sideBar->setVisible(visible);
616
    onSideBarVisibilityChanged();
617
618
}

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

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

void HelpPlugin::updateSideBarSource(const QUrl &newUrl)
{
640
641
    if (m_rightPaneSideBarWidget)
        m_rightPaneSideBarWidget->currentViewer()->setSource(newUrl);
642
643
}

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

652
653
void HelpPlugin::fontChanged()
{
654
    if (!m_rightPaneSideBarWidget)
655
        createRightPaneContextViewer();
656

657
658
    QVariant fontSetting = LocalHelpManager::engineFontSettings();
    QFont font = fontSetting.isValid() ? fontSetting.value<QFont>()
659
                                       : m_rightPaneSideBarWidget->currentViewer()->viewerFont();
660

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

669
670
671
672
673
674
675
676
677
678
679
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;
}

680
681
void HelpPlugin::setupHelpEngineIfNeeded()
{
kh1's avatar
kh1 committed
682
    m_helpManager->setEngineNeedsUpdate();
683
    if (ModeManager::currentMode() == m_mode
684
        || contextHelpOption() == Core::HelpManager::ExternalHelpAlways)
685
686
687
        m_helpManager->setupGuiHelpEngine();
}

688
bool HelpPlugin::canShowHelpSideBySide() const
con's avatar
con committed
689
{
690
    RightPanePlaceHolder *placeHolder = RightPanePlaceHolder::current();
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
    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;
}

HelpViewer *HelpPlugin::viewerForHelpViewerLocation(Core::HelpManager::HelpViewerLocation location)
{
    Core::HelpManager::HelpViewerLocation actualLocation = location;
    if (location == Core::HelpManager::SideBySideIfPossible)
        actualLocation = canShowHelpSideBySide() ? Core::HelpManager::SideBySideAlways
                                                 : Core::HelpManager::HelpModeAlways;

    if (actualLocation == Core::HelpManager::ExternalHelpAlways)
        return externalHelpViewer();
716

717
    if (actualLocation == Core::HelpManager::SideBySideAlways) {
718
        createRightPaneContextViewer();
719
        RightPaneWidget::instance()->setWidget(m_rightPaneSideBarWidget);
720
        RightPaneWidget::instance()->setShown(true);
721
        return m_rightPaneSideBarWidget->currentViewer();
kh's avatar
kh committed
722
    }
723

724
725
    QTC_CHECK(actualLocation == Core::HelpManager::HelpModeAlways);

726
727
728
729
    activateHelpMode(); // should trigger an createPage...
    HelpViewer *viewer = m_centralWidget->currentHelpViewer();
    if (!viewer)
        viewer = OpenPagesManager::instance().createPage();
730
731
732
    return viewer;
}

733
734
735
736
737
HelpViewer *HelpPlugin::viewerForContextHelp()
{
    return viewerForHelpViewerLocation(contextHelpOption());
}

738
739
740
741
742
743
static QUrl findBestLink(const QMap<QString, QUrl> &links, QString *highlightId)
{
    if (highlightId)
        highlightId->clear();
    if (links.isEmpty())
        return QUrl();
744
    QUrl source = links.constBegin().value();
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
    // 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;
}

766
void HelpPlugin::showContextHelp()
767
{
768
    if (ModeManager::currentMode() == m_mode)
769
770
771
        return;

    // Find out what to show
772
    QMap<QString, QUrl> links;
773
    QString idFromContext;
hjk's avatar
hjk committed
774
    if (IContext *context = Core::ICore::currentContextObject()) {
775
776
        idFromContext = context->contextHelpId();
        links = HelpManager::linksForIdentifier(idFromContext);
777
        // Maybe the id is already an URL
778
779
        if (links.isEmpty() && LocalHelpManager::isValidUrl(idFromContext))
            links.insert(idFromContext, idFromContext);
780
781
    }

782
    if (HelpViewer *viewer = viewerForContextHelp()) {
783
784
        QUrl source = findBestLink(links, &m_contextHelpHighlightId);
        if (!source.isValid()) {
kh's avatar
kh committed
785
            // No link found or no context object
786
            viewer->setSource(QUrl(Help::Constants::AboutBlank));
kh's avatar
kh committed
787
788
            viewer->setHtml(tr("<html><head><title>No Documentation</title>"
                "</head><body><br/><center><b>%1</b><br/>No documentation "
789
                "available.</center></body></html>").arg(idFromContext));
kh's avatar
kh committed
790
        } else {
791
792
793
            const QUrl &oldSource = viewer->source();
            if (source != oldSource) {
                viewer->stop();
794
                viewer->setSource(source); // triggers loadFinished which triggers id highlighting
795
            } else {
796
                viewer->scrollToAnchor(source.fragment());
797
            }
798
            viewer->setFocus();
799
            Core::ICore::raiseWindow(viewer);
con's avatar
con committed
800
801
802
803
804
805
        }
    }
}

void HelpPlugin::activateIndex()
{
hjk's avatar
hjk committed
806
    activateHelpMode();
con's avatar
con committed
807
808
809
810
811
    m_sideBar->activateItem(m_indexItem);
}

void HelpPlugin::activateContents()
{
hjk's avatar
hjk committed
812
    activateHelpMode();
con's avatar
con committed
813
814
815
816
817
    m_sideBar->activateItem(m_contentItem);
}

void HelpPlugin::activateSearch()
{
hjk's avatar
hjk committed
818
    activateHelpMode();
con's avatar
con committed
819
820
821
    m_sideBar->activateItem(m_searchItem);
}

822
823
824
825
826
827
void HelpPlugin::activateOpenPages()
{
    activateHelpMode();
    m_sideBar->activateItem(m_openPagesItem);
}

kh1's avatar
kh1 committed
828
829
830
831
832
833
void HelpPlugin::activateBookmarks()
{
    activateHelpMode();
    m_sideBar->activateItem(m_bookmarkItem);
}

834
Utils::StyledBar *HelpPlugin::createWidgetToolBar()
con's avatar
con committed
835
836
{
    m_filterComboBox = new QComboBox;
837
    m_filterComboBox->setMinimumContentsLength(15);
kh's avatar
kh committed
838
839
    connect(m_filterComboBox, SIGNAL(activated(QString)), this,
        SLOT(filterDocumentation(QString)));
840
841
842
843
    connect(m_filterComboBox, SIGNAL(currentIndexChanged(int)), this,
        SLOT(updateSideBarSource()));

    m_closeButton = new QToolButton();
844
    m_closeButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_BUTTON_CLOSE)));
Leena Miettinen's avatar
Leena Miettinen committed
845
    m_closeButton->setToolTip(tr("Close current page"));
846
847
848
849
    connect(m_closeButton, SIGNAL(clicked()), &OpenPagesManager::instance(),
        SLOT(closeCurrentPage()));
    connect(&OpenPagesManager::instance(), SIGNAL(pagesChanged()), this,
        SLOT(updateCloseButton()));
850
851
852
853
854
855
856
857
858
859
860
861

    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);
862
863
864
865

    return toolBar;
}

866
Utils::StyledBar *HelpPlugin::createIconToolBar(bool external)
867
{
868
869
    Utils::StyledBar *toolBar = new Utils::StyledBar;
    toolBar->setVisible(false);
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894

    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 {
895
896
897
898
        home = Core::ActionManager::command("Help.Home")->action();
        back = Core::ActionManager::command("Help.Previous")->action();
        next = Core