helpplugin.cpp 34 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
29

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

#include "bookmarkmanager.h"
#include "centralwidget.h"
#include "contentwindow.h"
con's avatar
con committed
35
36
#include "docsettingspage.h"
#include "filtersettingspage.h"
37
#include "generalsettingspage.h"
kh1's avatar
kh1 committed
38
#include "helpconstants.h"
39
#include "helpfindsupport.h"
con's avatar
con committed
40
#include "helpindexfilter.h"
kh1's avatar
kh1 committed
41
#include "helpmanager.h"
con's avatar
con committed
42
43
44
#include "helpmode.h"
#include "helpviewer.h"
#include "indexwindow.h"
45
46
#include "openpagesmanager.h"
#include "openpagesmodel.h"
con's avatar
con committed
47
48
#include "searchwidget.h"

49
#include <coreplugin/actionmanager/actionmanager.h>
50
51
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
52
53
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
54
#include <coreplugin/editormanager/ieditor.h>
55
56
#include <coreplugin/findplaceholder.h>
#include <coreplugin/icore.h>
kh1's avatar
kh1 committed
57
#include <coreplugin/helpmanager.h>
hjk's avatar
hjk committed
58
59
60
61
#include <coreplugin/minisplitter.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/rightpane.h>
#include <coreplugin/sidebar.h>
kh1's avatar
kh1 committed
62
#include <extensionsystem/pluginmanager.h>
63
#include <texteditor/texteditorconstants.h>
64
#include <utils/styledbar.h>
kh1's avatar
kh1 committed
65
#include <welcome/welcomemode.h>
66

con's avatar
con committed
67
#include <QtCore/QDir>
kh1's avatar
kh1 committed
68
#include <QtCore/QFileInfo>
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
69
70
#include <QtCore/QLibraryInfo>
#include <QtCore/QTranslator>
kh1's avatar
kh1 committed
71
72
#include <QtCore/qplugin.h>

con's avatar
con committed
73
#include <QtGui/QAction>
kh1's avatar
kh1 committed
74
75
#include <QtGui/QComboBox>
#include <QtGui/QDesktopServices>
con's avatar
con committed
76
77
78
#include <QtGui/QShortcut>
#include <QtGui/QSplitter>
#include <QtGui/QToolBar>
kh1's avatar
kh1 committed
79

con's avatar
con committed
80
81
#include <QtHelp/QHelpEngine>

82
83
84
85
86
87
#if !defined(QT_NO_WEBKIT)
#include <QtWebKit/QWebElement>
#include <QtWebKit/QWebElementCollection>
#include <QtWebKit/QWebFrame>
#endif

88
using namespace Core::Constants;
con's avatar
con committed
89
90
using namespace Help::Internal;

91
92
93
94
95
const char * const SB_INDEX = QT_TRANSLATE_NOOP("Help::Internal::HelpPlugin", "Index");
const char * const SB_CONTENTS = QT_TRANSLATE_NOOP("Help::Internal::HelpPlugin", "Contents");
const char * const SB_BOOKMARKS = QT_TRANSLATE_NOOP("Help::Internal::HelpPlugin", "Bookmarks");
const char * const SB_SEARCH = QT_TRANSLATE_NOOP("Help::Internal::HelpPlugin", "Search");

96
97
const char * const SB_OPENPAGES = "OpenPages";

98
#define IMAGEPATH ":/help/images/"
99
100
101
#if defined(Q_OS_MAC)
#   define DOCPATH "/../Resources/doc/"
#else
Tobias Hunger's avatar
Tobias Hunger committed
102
#   define DOCPATH "/../share/doc/qtcreator/"
103
104
#endif

kh1's avatar
kh1 committed
105
HelpPlugin::HelpPlugin()
106
107
    : m_mode(0),
    m_core(0),
con's avatar
con committed
108
109
110
111
112
113
    m_centralWidget(0),
    m_helpViewerForSideBar(0),
    m_contentItem(0),
    m_indexItem(0),
    m_searchItem(0),
    m_bookmarkItem(0),
114
115
    m_sideBar(0),
    m_firstModeChange(true)
con's avatar
con committed
116
117
118
119
120
121
122
{
}

HelpPlugin::~HelpPlugin()
{
}

123
bool HelpPlugin::initialize(const QStringList &arguments, QString *error)
con's avatar
con committed
124
{
125
126
    Q_UNUSED(arguments)
    Q_UNUSED(error)
127
    m_core = Core::ICore::instance();
128
    Core::Context globalcontext(Core::Constants::C_GLOBAL);
129
    Core::Context modecontext(Constants::C_MODE_HELP);
con's avatar
con committed
130

131
    const QString &locale = qApp->property("qtc_locale").toString();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
132
133
    if (!locale.isEmpty()) {
        QTranslator *qtr = new QTranslator(this);
134
        QTranslator *qhelptr = new QTranslator(this);
135
136
        const QString &creatorTrPath = Core::ICore::instance()->resourcePath()
            + 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
    }

kh1's avatar
kh1 committed
146
    addAutoReleasedObject(m_helpManager = new LocalHelpManager(this));
147
    addAutoReleasedObject(m_openPagesManager = new OpenPagesManager(this));
148
149
    addAutoReleasedObject(m_docSettingsPage = new DocSettingsPage());
    addAutoReleasedObject(m_filterSettingsPage = new FilterSettingsPage());
150
    addAutoReleasedObject(m_generalSettingsPage = new GeneralSettingsPage());
151

152
153
    connect(m_generalSettingsPage, SIGNAL(fontChanged()), this,
        SLOT(fontChanged()));
kh1's avatar
kh1 committed
154
    connect(Core::HelpManager::instance(), SIGNAL(helpRequested(QUrl)), this,
kh1's avatar
kh1 committed
155
        SLOT(handleHelpRequest(QUrl)));
kh1's avatar
kh1 committed
156
    m_filterSettingsPage->setHelpManager(m_helpManager);
157
158
    connect(m_filterSettingsPage, SIGNAL(filtersChanged()), this,
        SLOT(setupHelpEngineIfNeeded()));
kh1's avatar
kh1 committed
159
    connect(Core::HelpManager::instance(), SIGNAL(documentationChanged()), this,
160
        SLOT(setupHelpEngineIfNeeded()));
con's avatar
con committed
161

162
163
    m_splitter = new Core::MiniSplitter;
    m_centralWidget = new Help::Internal::CentralWidget();
164
165
    connect(m_centralWidget, SIGNAL(sourceChanged(QUrl)), this,
        SLOT(updateSideBarSource(QUrl)));
con's avatar
con committed
166
167

    // Add Home, Previous and Next actions (used in the toolbar)
168
169
    QAction *action = new QAction(QIcon(QLatin1String(IMAGEPATH "home.png")),
        tr("Home"), this);
170
    Core::ActionManager *am = m_core->actionManager();
171
172
173
    Core::Command *cmd = am->registerAction(action, QLatin1String("Help.Home"),
        globalcontext);
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(home()));
con's avatar
con committed
174

175
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "previous.png")),
176
        tr("Previous Page"), this);
177
    cmd = am->registerAction(action, QLatin1String("Help.Previous"), modecontext);
178
    cmd->setDefaultKeySequence(QKeySequence::Back);
179
180
181
182
    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
183

184
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "next.png")), tr("Next Page"),
kh's avatar
kh committed
185
        this);
186
    cmd = am->registerAction(action, QLatin1String("Help.Next"), modecontext);
187
    cmd->setDefaultKeySequence(QKeySequence::Forward);
188
189
190
191
    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
192

193
    action = new QAction(QIcon(QLatin1String(IMAGEPATH "bookmark.png")),
con's avatar
con committed
194
        tr("Add Bookmark"), this);
195
    cmd = am->registerAction(action, QLatin1String("Help.AddBookmark"),
kh's avatar
kh committed
196
        modecontext);
con's avatar
con committed
197
    cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_M));
198
    connect(action, SIGNAL(triggered()), this, SLOT(addBookmark()));
con's avatar
con committed
199

200
    // Add Contents, Index, and Context menu items and a separator to the Help menu
201
    action = new QAction(QIcon::fromTheme(QLatin1String("help-contents")), tr(SB_CONTENTS), this);
202
203
204
205
    cmd = am->registerAction(action, QLatin1String("Help.Contents"), globalcontext);
    am->actionContainer(M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
    connect(action, SIGNAL(triggered()), this, SLOT(activateContents()));

206
    action = new QAction(tr(SB_INDEX), this);
207
208
209
210
211
212
213
    cmd = am->registerAction(action, QLatin1String("Help.Index"), globalcontext);
    am->actionContainer(M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
    connect(action, SIGNAL(triggered()), this, SLOT(activateIndex()));

    action = new QAction(tr("Context Help"), this);
    cmd = am->registerAction(action, QLatin1String("Help.Context"), globalcontext);
    am->actionContainer(M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
con's avatar
con committed
214
    cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F1));
215
    connect(action, SIGNAL(triggered()), this, SLOT(activateContext()));
con's avatar
con committed
216

Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
217
#ifndef Q_WS_MAC
218
219
220
221
    action = new QAction(this);
    action->setSeparator(true);
    cmd = am->registerAction(action, QLatin1String("Help.Separator"), globalcontext);
    am->actionContainer(M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
con's avatar
con committed
222
223
#endif

224
225
226
    action = new QAction(this);
    am->registerAction(action, Core::Constants::PRINT, modecontext);
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(print()));
con's avatar
con committed
227

228
229
230
231
232
    action = new QAction(this);
    cmd = am->registerAction(action, Core::Constants::COPY, modecontext);
    connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(copy()));
    action->setText(cmd->action()->text());
    action->setIcon(cmd->action()->icon());
233

234
    if (Core::ActionContainer *advancedMenu = am->actionContainer(M_EDIT_ADVANCED)) {
235
        // reuse TextEditor constants to avoid a second pair of menu actions
236
237
        action = new QAction(tr("Increase Font Size"), this);
        cmd = am->registerAction(action, TextEditor::Constants::INCREASE_FONT_SIZE,
238
239
            modecontext);
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl++")));
240
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(zoomIn()));
241
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
242

243
244
        action = new QAction(tr("Decrease Font Size"), this);
        cmd = am->registerAction(action, TextEditor::Constants::DECREASE_FONT_SIZE,
245
246
            modecontext);
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+-")));
247
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(zoomOut()));
248
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
249

250
251
        action = new QAction(tr("Reset Font Size"), this);
        cmd = am->registerAction(action, TextEditor::Constants::RESET_FONT_SIZE,
252
            modecontext);
253
#ifndef Q_WS_MAC
254
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+0")));
255
#endif
256
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(resetZoom()));
257
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
258
259
    }

260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
    if (Core::ActionContainer *windowMenu = am->actionContainer(M_WINDOW)) {
        // reuse EditorManager constants to avoid a second pair of menu actions
        action = new QAction(QApplication::tr("EditorManager",
            "Next Open Document in History"), this);
        Core::Command *ctrlTab = am->registerAction(action, GOTOPREVINHISTORY,
            modecontext);   // Goto Previous In History Action
        windowMenu->addAction(ctrlTab, Core::Constants::G_WINDOW_NAVIGATE);
        connect(action, SIGNAL(triggered()), &OpenPagesManager::instance(),
            SLOT(gotoPreviousPage()));

        action = new QAction(QApplication::tr("EditorManager",
            "Previous Open Document in History"), this);
        Core::Command *ctrlShiftTab = am->registerAction(action, GOTONEXTINHISTORY,
            modecontext);   // Goto Next In History Action
        windowMenu->addAction(ctrlShiftTab, Core::Constants::G_WINDOW_NAVIGATE);
        connect(action, SIGNAL(triggered()), &OpenPagesManager::instance(),
            SLOT(gotoNextPage()));

#ifdef Q_WS_MAC
        ctrlTab->setDefaultKeySequence(QKeySequence(tr("Alt+Tab")));
        ctrlShiftTab->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+Tab")));
#else
        ctrlTab->setDefaultKeySequence(QKeySequence(tr("Ctrl+Tab")));
        ctrlShiftTab->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Tab")));
#endif
    }

287
288
289
290
    Aggregation::Aggregate *agg = new Aggregation::Aggregate;
    agg->add(m_centralWidget);
    agg->add(new HelpFindSupport(m_centralWidget));
    m_mainWidget = new QWidget;
con's avatar
con committed
291
    m_splitter->addWidget(m_mainWidget);
292
293
294
295
296
297
    QVBoxLayout *mainWidgetLayout = new QVBoxLayout(m_mainWidget);
    mainWidgetLayout->setMargin(0);
    mainWidgetLayout->setSpacing(0);
    mainWidgetLayout->addWidget(createToolBar());
    mainWidgetLayout->addWidget(m_centralWidget);

298
299
300
301
302
    HelpIndexFilter *helpIndexFilter = new HelpIndexFilter();
    addAutoReleasedObject(helpIndexFilter);
    connect(helpIndexFilter, SIGNAL(linkActivated(QUrl)), this,
        SLOT(switchToHelpMode(QUrl)));

303
304
305
306
307
    QDesktopServices::setUrlHandler("qthelp", this, "handleHelpRequest");
    connect(m_core->modeManager(), SIGNAL(currentModeChanged(Core::IMode*)),
        this, SLOT(modeChanged(Core::IMode*)));

    addAutoReleasedObject(m_mode = new HelpMode(m_splitter, m_centralWidget));
308
    m_mode->setContext(modecontext);
309

con's avatar
con committed
310
311
312
    return true;
}

313
void HelpPlugin::extensionsInitialized()
314
{
kh1's avatar
kh1 committed
315
    const QString &nsInternal = QString::fromLatin1("com.nokia.qtcreator.%1%2%3")
316
317
        .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR).arg(IDE_VERSION_RELEASE);

kh1's avatar
kh1 committed
318
319
    Core::HelpManager *helpManager = Core::HelpManager::instance();
    foreach (const QString &ns, helpManager->registeredNamespaces()) {
320
        if (ns.startsWith(QLatin1String("com.nokia.qtcreator."))
kh1's avatar
kh1 committed
321
322
            && ns != nsInternal)
            helpManager->unregisterDocumentation(QStringList() << ns);
323
324
325
326
327
    }

    QStringList filesToRegister;
    // Explicitly register qml.qch if located in creator directory. This is only
    // needed for the creator-qml package, were we want to ship the documentation
kh1's avatar
kh1 committed
328
    // without a qt development version. TODO: is this still really needed, remove
329
330
331
332
333
334
335
    const QString &appPath = QCoreApplication::applicationDirPath();
    filesToRegister.append(QDir::cleanPath(QDir::cleanPath(appPath
        + QLatin1String(DOCPATH "qml.qch"))));

    // we might need to register creators inbuild help
    filesToRegister.append(QDir::cleanPath(appPath
        + QLatin1String(DOCPATH "qtcreator.qch")));
336
    helpManager->registerDocumentation(filesToRegister);
337
338
}

339
ExtensionSystem::IPlugin::ShutdownFlag HelpPlugin::aboutToShutdown()
340
{
341
342
    if (m_sideBar)
        m_sideBar->saveSettings(m_core->settings(), QLatin1String("HelpSideBar"));
343
    return SynchronousShutdown;
344
345
}

346
void HelpPlugin::setupUi()
347
{
348
349
    // side bar widgets and shortcuts
    Core::ActionManager *am = m_core->actionManager();
350
    Core::Context modecontext(Constants::C_MODE_HELP);
351
352

    IndexWindow *indexWindow = new IndexWindow();
353
    indexWindow->setWindowTitle(tr(SB_INDEX));
354
    m_indexItem = new Core::SideBarItem(indexWindow, QLatin1String(SB_INDEX));
355
356
357
358
359

    connect(indexWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));
    connect(indexWindow, SIGNAL(linksActivated(QMap<QString, QUrl>, QString)),
        m_centralWidget, SLOT(showTopicChooser(QMap<QString, QUrl>, QString)));
360
361
362
363

    QMap<QString, Core::Command*> shortcutMap;
    QShortcut *shortcut = new QShortcut(m_splitter);
    shortcut->setWhatsThis(tr("Activate Index in Help mode"));
364
365
366
    Core::Command* cmd = am->registerShortcut(shortcut,
        QLatin1String("Help.IndexShortcut"), modecontext);
    cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_I));
367
    connect(shortcut, SIGNAL(activated()), this, SLOT(activateIndex()));
368
    shortcutMap.insert(QLatin1String(SB_INDEX), cmd);
369
370

    ContentWindow *contentWindow = new ContentWindow();
371
    contentWindow->setWindowTitle(tr(SB_CONTENTS));
372
    m_contentItem = new Core::SideBarItem(contentWindow, QLatin1String(SB_CONTENTS));
373
374
    connect(contentWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));
375
376
377

    shortcut = new QShortcut(m_splitter);
    shortcut->setWhatsThis(tr("Activate Contents in Help mode"));
378
379
380
    cmd = am->registerShortcut(shortcut, QLatin1String("Help.ContentsShortcut"),
        modecontext);
    cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C));
381
    connect(shortcut, SIGNAL(activated()), this, SLOT(activateContents()));
382
    shortcutMap.insert(QLatin1String(SB_CONTENTS), cmd);
383
384

    SearchWidget *searchWidget = new SearchWidget();
385
386
    searchWidget->setWindowTitle(tr(SB_SEARCH));
    m_searchItem = new Core::SideBarItem(searchWidget, QLatin1String(SB_SEARCH));
387
388
    connect(searchWidget, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSourceFromSearch(QUrl)));
389

kh1's avatar
kh1 committed
390
391
392
393
394
395
     shortcut = new QShortcut(m_splitter);
     shortcut->setWhatsThis(tr("Activate Search in Help mode"));
     cmd = am->registerShortcut(shortcut, QLatin1String("Help.SearchShortcut"),
         modecontext);
     cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Slash));
     connect(shortcut, SIGNAL(activated()), this, SLOT(activateSearch()));
con's avatar
con committed
396
     shortcutMap.insert(QLatin1String(SB_SEARCH), cmd);
397

kh1's avatar
kh1 committed
398
    BookmarkManager *manager = &LocalHelpManager::bookmarkManager();
399
    BookmarkWidget *bookmarkWidget = new BookmarkWidget(manager, 0, false);
400
    bookmarkWidget->setWindowTitle(tr(SB_BOOKMARKS));
401
    m_bookmarkItem = new Core::SideBarItem(bookmarkWidget, QLatin1String(SB_BOOKMARKS));
402
403
    connect(bookmarkWidget, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));
404

kh1's avatar
kh1 committed
405
406
407
408
409
410
     shortcut = new QShortcut(m_splitter);
     shortcut->setWhatsThis(tr("Activate Bookmarks in Help mode"));
     cmd = am->registerShortcut(shortcut, QLatin1String("Help.BookmarkShortcut"),
         modecontext);
     cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B));
     connect(shortcut, SIGNAL(activated()), this, SLOT(activateBookmarks()));
con's avatar
con committed
411
     shortcutMap.insert(QLatin1String(SB_BOOKMARKS), cmd);
412

413
414
    QWidget *openPagesWidget = OpenPagesManager::instance().openPagesWidget();
    openPagesWidget->setWindowTitle(tr("Open Pages"));
415
    m_openPagesItem = new Core::SideBarItem(openPagesWidget, QLatin1String(SB_OPENPAGES));
416
417
418
419
420
421
422

    shortcut = new QShortcut(m_splitter);
    shortcut->setWhatsThis(tr("Activate Open Pages in Help mode"));
    cmd = am->registerShortcut(shortcut, QLatin1String("Help.PagesShortcut"),
        modecontext);
    cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
    connect(shortcut, SIGNAL(activated()), this, SLOT(activateOpenPages()));
423
    shortcutMap.insert(QLatin1String(SB_OPENPAGES), cmd);
424

425
    QList<Core::SideBarItem*> itemList;
426
427
428
    itemList << m_contentItem << m_indexItem << m_searchItem << m_bookmarkItem
        << m_openPagesItem;
    m_sideBar = new Core::SideBar(itemList, QList<Core::SideBarItem*>()
kh1's avatar
kh1 committed
429
        << m_contentItem << m_openPagesItem);
430
431
432
    m_sideBar->setShortcutMap(shortcutMap);

    m_splitter->setOpaqueResize(false);
con's avatar
con committed
433
    m_splitter->insertWidget(0, m_sideBar);
434
435
436
437
    m_splitter->setStretchFactor(0, 0);
    m_splitter->setStretchFactor(1, 1);
    m_splitter->setSizes(QList<int>() << 300 << 300);
    m_sideBar->readSettings(m_core->settings(), QLatin1String("HelpSideBar"));
438
439
}

kh1's avatar
kh1 committed
440
441
void HelpPlugin::resetFilter()
{
kh1's avatar
kh1 committed
442
443
444
445
446
447
448
449
450
451
452
    const QString &filterInternal = QString::fromLatin1("Qt Creator %1.%2.%3")
        .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR).arg(IDE_VERSION_RELEASE);
    const QRegExp filterRegExp(QLatin1String("Qt Creator \\d*\\.\\d*\\.\\d*"));

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

kh1's avatar
kh1 committed
453
454
    const QLatin1String weAddedFilterKey("UnfilteredFilterInserted");
    const QLatin1String previousFilterNameKey("UnfilteredFilterName");
kh1's avatar
kh1 committed
455
    if (engine->customValue(weAddedFilterKey).toInt() == 1) {
kh1's avatar
kh1 committed
456
        // we added a filter at some point, remove previously added filter
kh1's avatar
kh1 committed
457
        const QString &filter = engine->customValue(previousFilterNameKey).toString();
458
        if (!filter.isEmpty())
kh1's avatar
kh1 committed
459
            engine->removeCustomFilter(filter);
kh1's avatar
kh1 committed
460
461
462
463
    }

    // potentially remove a filter with new name
    const QString filterName = tr("Unfiltered");
kh1's avatar
kh1 committed
464
465
466
467
468
469
470
471
    engine->removeCustomFilter(filterName);
    engine->addCustomFilter(filterName, QStringList());
    engine->setCustomValue(weAddedFilterKey, 1);
    engine->setCustomValue(previousFilterNameKey, filterName);
    engine->setCurrentFilter(filterName);

    updateFilterComboBox();
    connect(engine, SIGNAL(setupFinished()), this, SLOT(updateFilterComboBox()));
472
473
}

474
void HelpPlugin::createRightPaneContextViewer()
con's avatar
con committed
475
{
476
477
    if (m_helpViewerForSideBar)
        return;
con's avatar
con committed
478

479
480
    QAction *switchToHelp = new QAction(tr("Go to Help Mode"), this);
    connect(switchToHelp, SIGNAL(triggered()), this, SLOT(switchToHelpMode()));
con's avatar
con committed
481

482
    QAction *next = new QAction(QIcon(QLatin1String(IMAGEPATH "next.png")),
483
        tr("Next"), this);
484
    QAction *previous = new QAction(QIcon(QLatin1String(IMAGEPATH "previous.png")),
485
        tr("Previous"), this);
con's avatar
con committed
486
487
488
489
490

    // Dummy layout to align the close button to the right
    QHBoxLayout *hboxLayout = new QHBoxLayout();
    hboxLayout->setSpacing(0);
    hboxLayout->setMargin(0);
491
492
493
494
495
496
497

    // left side actions
    QToolBar *rightPaneToolBar = new QToolBar();
    rightPaneToolBar->addAction(switchToHelp);
    rightPaneToolBar->addAction(previous);
    rightPaneToolBar->addAction(next);

498
    hboxLayout->addWidget(rightPaneToolBar);
499
500
501
502
    hboxLayout->addStretch();

    QToolButton *closeButton = new QToolButton();
    closeButton->setIcon(QIcon(":/core/images/closebutton.png"));
con's avatar
con committed
503
504
    connect(closeButton, SIGNAL(clicked()), this, SLOT(slotHideRightPane()));

505
506
507
    // close button to the right
    hboxLayout->addWidget(closeButton);

con's avatar
con committed
508
509
510
511
    QVBoxLayout *rightPaneLayout = new QVBoxLayout;
    rightPaneLayout->setMargin(0);
    rightPaneLayout->setSpacing(0);

512
513
514
515
516
517
518
519
    QWidget *rightPaneSideBar = new QWidget;
    rightPaneSideBar->setLayout(rightPaneLayout);
    addAutoReleasedObject(new Core::BaseRightPaneWidget(rightPaneSideBar));

    Utils::StyledBar *rightPaneStyledBar = new Utils::StyledBar;
    rightPaneStyledBar->setLayout(hboxLayout);
    rightPaneLayout->addWidget(rightPaneStyledBar);

520
    m_helpViewerForSideBar = new HelpViewer(qreal(0.0), rightPaneSideBar);
521
522
523
524
    rightPaneLayout->addWidget(m_helpViewerForSideBar);
    rightPaneLayout->addWidget(new Core::FindToolBarPlaceHolder(rightPaneSideBar));
    rightPaneSideBar->setFocusProxy(m_helpViewerForSideBar);

con's avatar
con committed
525
526
527
    Aggregation::Aggregate *agg = new Aggregation::Aggregate();
    agg->add(m_helpViewerForSideBar);
    agg->add(new HelpViewerFindSupport(m_helpViewerForSideBar));
528
529
    m_core->addContextObject(new Core::BaseContext(m_helpViewerForSideBar,
        Core::Context(Constants::C_HELP_SIDEBAR), this));
530

531
532
    QAction *copy = new QAction(this);
    Core::Command *cmd = m_core->actionManager()->registerAction(copy, Core::Constants::COPY,
533
        Core::Context(Constants::C_HELP_SIDEBAR));
534
535
    copy->setText(cmd->action()->text());
    copy->setIcon(cmd->action()->icon());
536

537
538
539
    connect(copy, SIGNAL(triggered()), m_helpViewerForSideBar, SLOT(copy()));
    connect(next, SIGNAL(triggered()), m_helpViewerForSideBar, SLOT(forward()));
    connect(previous, SIGNAL(triggered()), m_helpViewerForSideBar, SLOT(backward()));
kh1's avatar
kh1 committed
540
541
542
543

    // 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();
con's avatar
con committed
544
545
}

hjk's avatar
hjk committed
546
547
548
549
550
void HelpPlugin::activateHelpMode()
{
    m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP));
}

con's avatar
con committed
551
552
553
554
555
556
557
void HelpPlugin::switchToHelpMode()
{
    switchToHelpMode(m_helpViewerForSideBar->source());
}

void HelpPlugin::switchToHelpMode(const QUrl &source)
{
hjk's avatar
hjk committed
558
    activateHelpMode();
con's avatar
con committed
559
560
561
562
563
564
565
566
567
568
569
    m_centralWidget->setSource(source);
    m_centralWidget->setFocus();
}

void HelpPlugin::slotHideRightPane()
{
    Core::RightPaneWidget::instance()->setShown(false);
}

void HelpPlugin::modeChanged(Core::IMode *mode)
{
570
571
    if (mode == m_mode && m_firstModeChange) {
        m_firstModeChange = false;
572

con's avatar
con committed
573
574
        qApp->processEvents();
        qApp->setOverrideCursor(Qt::WaitCursor);
575

kh1's avatar
kh1 committed
576
        m_helpManager->setupGuiHelpEngine();
577
        setupUi();
kh1's avatar
kh1 committed
578
        resetFilter();
579
        OpenPagesManager::instance().setupInitialPages();
580

581
582
583
584
        qApp->restoreOverrideCursor();
    } else if (mode == m_mode && !m_firstModeChange) {
        qApp->setOverrideCursor(Qt::WaitCursor);
        m_helpManager->setupGuiHelpEngine();
con's avatar
con committed
585
586
587
588
        qApp->restoreOverrideCursor();
    }
}

589
590
void HelpPlugin::updateSideBarSource()
{
591
592
593
594
595
    if (HelpViewer *viewer = m_centralWidget->currentHelpViewer()) {
        const QUrl &url = viewer->source();
        if (url.isValid())
            updateSideBarSource(url);
    }
596
597
598
599
600
601
602
603
}

void HelpPlugin::updateSideBarSource(const QUrl &newUrl)
{
    if (m_helpViewerForSideBar)
        m_helpViewerForSideBar->setSource(newUrl);
}

604
void HelpPlugin::updateCloseButton()
605
{
606
607
    m_closeButton->setEnabled(OpenPagesManager::instance().pageCount() > 1);
}
608

609
610
611
612
void HelpPlugin::fontChanged()
{
    if (!m_helpViewerForSideBar)
        createRightPaneContextViewer();
613

kh1's avatar
kh1 committed
614
    const QHelpEngine &engine = LocalHelpManager::helpEngine();
615
616
617
618
619
620
621
622
    QFont font = qVariantValue<QFont>(engine.customValue(QLatin1String("font"),
        m_helpViewerForSideBar->viewerFont()));

    m_helpViewerForSideBar->setFont(font);
    const int count = OpenPagesManager::instance().pageCount();
    for (int i = 0; i < count; ++i) {
        if (HelpViewer *viewer = CentralWidget::instance()->viewerAt(i))
            viewer->setViewerFont(font);
623
624
625
    }
}

626
627
void HelpPlugin::setupHelpEngineIfNeeded()
{
kh1's avatar
kh1 committed
628
629
    m_helpManager->setEngineNeedsUpdate();
    if (Core::ModeManager::instance()->currentMode() == m_mode)
630
631
632
        m_helpManager->setupGuiHelpEngine();
}

633
HelpViewer* HelpPlugin::viewerForContextMode()
con's avatar
con committed
634
{
635
    using namespace Core;
636

637
    bool showSideBySide = false;
kh1's avatar
kh1 committed
638
    const QHelpEngineCore &engine = LocalHelpManager::helpEngine();
639
    RightPanePlaceHolder *placeHolder = RightPanePlaceHolder::current();
640
    switch (engine.customValue(QLatin1String("ContextHelpOption"), 0).toInt()) {
641
642
643
644
        case 0: {
            // side by side if possible
            if (IEditor *editor = EditorManager::instance()->currentEditor()) {
                if (!placeHolder || !placeHolder->isVisible()) {
645
646
647
648
649
                    if (!editor->widget())
                        break;
                    if (!editor->widget()->isVisible())
                        break;
                    if (editor->widget()->width() < 800)
650
651
                        break;
                }
652
            }
653
654
655
656
657
        }   // fall through
        case 1: {
            // side by side
            showSideBySide = true;
        }   break;
658

659
660
        default: // help mode
            break;
661
662
    }

663
    HelpViewer *viewer = m_centralWidget->currentHelpViewer();
664
    if (placeHolder && showSideBySide) {
665
        RightPaneWidget::instance()->setShown(true);
666
667

        createRightPaneContextViewer();
kh's avatar
kh committed
668
        viewer = m_helpViewerForSideBar;
con's avatar
con committed
669
    } else {
670
        activateHelpMode();
671
        if (!viewer)
672
            viewer = OpenPagesManager::instance().createPage();
kh's avatar
kh committed
673
    }
674
675
676
677
678
    return viewer;
}

void HelpPlugin::activateContext()
{
679
    using namespace Core;
680
    createRightPaneContextViewer();
681

682
    RightPanePlaceHolder* placeHolder = RightPanePlaceHolder::current();
683
684
685
686
687
688
689
    if (placeHolder && m_helpViewerForSideBar->hasFocus()) {
        switchToHelpMode();
        return;
    } else if (m_core->modeManager()->currentMode() == m_mode)
        return;

    // Find out what to show
690
    QMap<QString, QUrl> links;
691
    if (IContext *context = m_core->currentContextObject()) {
692
693
        m_idFromContext = context->contextHelpId();
        links = Core::HelpManager::instance()->linksForIdentifier(m_idFromContext);
694
695
    }

696
    if (HelpViewer* viewer = viewerForContextMode()) {
kh's avatar
kh committed
697
698
699
700
        if (links.isEmpty()) {
            // No link found or no context object
            viewer->setHtml(tr("<html><head><title>No Documentation</title>"
                "</head><body><br/><center><b>%1</b><br/>No documentation "
701
                "available.</center></body></html>").arg(m_idFromContext));
con's avatar
con committed
702
            viewer->setSource(QUrl());
kh's avatar
kh committed
703
        } else {
704
            const QUrl &source = *links.begin();
705
706
            const QUrl &oldSource = viewer->source();
            if (source != oldSource) {
kh1's avatar
kh1 committed
707
#if !defined(QT_NO_WEBKIT)
708
                viewer->stop();
kh1's avatar
kh1 committed
709
#endif
kh's avatar
kh committed
710
                viewer->setSource(source);
711
            }
kh's avatar
kh committed
712
            viewer->setFocus();
713
714
715
716
717
718
719
            connect(viewer, SIGNAL(loadFinished(bool)), this,
                SLOT(highlightSearchTerms()));

            if (source.toString().remove(source.fragment())
                == oldSource.toString().remove(oldSource.fragment())) {
                    highlightSearchTerms();
            }
con's avatar
con committed
720
721
722
723
724
725
        }
    }
}

void HelpPlugin::activateIndex()
{
hjk's avatar
hjk committed
726
    activateHelpMode();
con's avatar
con committed
727
728
729
730
731
    m_sideBar->activateItem(m_indexItem);
}

void HelpPlugin::activateContents()
{
hjk's avatar
hjk committed
732
    activateHelpMode();
con's avatar
con committed
733
734
735
736
737
    m_sideBar->activateItem(m_contentItem);
}

void HelpPlugin::activateSearch()
{
hjk's avatar
hjk committed
738
    activateHelpMode();
con's avatar
con committed
739
740
741
    m_sideBar->activateItem(m_searchItem);
}

742
743
744
745
746
747
void HelpPlugin::activateOpenPages()
{
    activateHelpMode();
    m_sideBar->activateItem(m_openPagesItem);
}

kh1's avatar
kh1 committed
748
749
750
751
752
753
void HelpPlugin::activateBookmarks()
{
    activateHelpMode();
    m_sideBar->activateItem(m_bookmarkItem);
}

con's avatar
con committed
754
755
756
QToolBar *HelpPlugin::createToolBar()
{
    QToolBar *toolWidget = new QToolBar;
757
    Core::ActionManager *am = m_core->actionManager();
con's avatar
con committed
758
759
760
761
762
763
764
765
766
767
    toolWidget->addAction(am->command(QLatin1String("Help.Home"))->action());
    toolWidget->addAction(am->command(QLatin1String("Help.Previous"))->action());
    toolWidget->addAction(am->command(QLatin1String("Help.Next"))->action());
    toolWidget->addSeparator();
    toolWidget->addAction(am->command(QLatin1String("Help.AddBookmark"))->action());
    toolWidget->setMovable(false);

    toolWidget->addSeparator();

    QWidget *w = new QWidget;
768
769
    toolWidget->addWidget(w);

con's avatar
con committed
770
771
772
    QHBoxLayout *layout = new QHBoxLayout(w);
    layout->setMargin(0);
    layout->addSpacing(10);
773
    layout->addWidget(OpenPagesManager::instance().openPagesComboBox());
774

con's avatar
con committed
775
776
777
    layout->addWidget(new QLabel(tr("Filtered by:")));
    m_filterComboBox = new QComboBox;
    m_filterComboBox->setMinimumContentsLength(20);
778
    layout->addWidget(m_filterComboBox);
kh's avatar
kh committed
779
780
    connect(m_filterComboBox, SIGNAL(activated(QString)), this,
        SLOT(filterDocumentation(QString)));
781
782
783
784
785
786
787
788
789
790
791
792
    connect(m_filterComboBox, SIGNAL(currentIndexChanged(int)), this,
        SLOT(updateSideBarSource()));

    m_closeButton = new QToolButton();
    m_closeButton->setIcon(QIcon(":/core/images/closebutton.png"));
    m_closeButton->setToolTip(tr("Close current Page"));
    connect(m_closeButton, SIGNAL(clicked()), &OpenPagesManager::instance(),
        SLOT(closeCurrentPage()));
    connect(&OpenPagesManager::instance(), SIGNAL(pagesChanged()), this,
        SLOT(updateCloseButton()));
    layout->addStretch();
    layout->addWidget(m_closeButton);
con's avatar
con committed
793
794
795
796
797
798

    return toolWidget;
}

void HelpPlugin::updateFilterComboBox()
{
kh1's avatar
kh1 committed
799
    const QHelpEngine &engine = LocalHelpManager::helpEngine();
con's avatar
con committed
800
801
    QString curFilter = m_filterComboBox->currentText();
    if (curFilter.isEmpty())
802
        curFilter = engine.currentFilter();
con's avatar
con committed
803
    m_filterComboBox->clear();
804
    m_filterComboBox->addItems(engine.customFilters());
con's avatar
con committed
805
806
807
808
809
810
811
812
    int idx = m_filterComboBox->findText(curFilter);
    if (idx < 0)
        idx = 0;
    m_filterComboBox->setCurrentIndex(idx);
}

void HelpPlugin::filterDocumentation(const QString &customFilter)
{
kh1's avatar
kh1 committed
813
    LocalHelpManager::helpEngine().setCurrentFilter(customFilter);
con's avatar
con committed
814
815
816
817
}

void HelpPlugin::addBookmark()
{
818
    HelpViewer *viewer = m_centralWidget->currentHelpViewer();
con's avatar
con committed
819

820
821
    const QString &url = viewer->source().toString();
    if (url.isEmpty() || url == Help::Constants::AboutBlank)
con's avatar
con committed
822
823
        return;

kh1's avatar
kh1 committed
824
    BookmarkManager *manager = &LocalHelpManager::bookmarkManager();
825
    manager->showBookmarkDialog(m_centralWidget, viewer->title(), url);
con's avatar
con committed
826
827
}

828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
void HelpPlugin::highlightSearchTerms()
{
    if (HelpViewer* viewer = viewerForContextMode()) {
        disconnect(viewer, SIGNAL(loadFinished(bool)), this,
            SLOT(highlightSearchTerms()));

#if !defined(QT_NO_WEBKIT)
        const QString &attrValue = m_idFromContext.mid(m_idFromContext
            .lastIndexOf(QChar(':')) + 1);
        if (attrValue.isEmpty())
            return;

        const QWebElement &document = viewer->page()->mainFrame()->documentElement();
        const QWebElementCollection &collection = document.findAll(QLatin1String("h3.fn a"));

        const QLatin1String property("background-color");
        foreach (const QWebElement &element, collection) {
            const QString &name = element.attribute(QLatin1String("name"));
            if (name.isEmpty())
                continue;

            if (m_oldAttrValue == name) {
                QWebElement parent = element.parent();
                parent.setStyleProperty(property, m_styleProperty);
            }

            if (attrValue == name) {
                QWebElement parent = element.parent();
                m_styleProperty = parent.styleProperty(property,
                    QWebElement::InlineStyle);
                parent.setStyleProperty(property, QLatin1String("yellow"));
            }
        }
        m_oldAttrValue = attrValue;
#endif
        viewer->findText(m_idFromContext, 0, false, true);
    }
}

kh1's avatar
kh1 committed
867
void HelpPlugin::handleHelpRequest(const QUrl &url)
con's avatar
con committed
868
{
kh1's avatar
kh1 committed
869
    if (HelpViewer::launchWithExternalApp(url))
870
871
        return;

872
873
    QString address = url.toString();
    if (!Core::HelpManager::instance()->findFile(url).isValid()) {
kh1's avatar
kh1 committed
874
875
876
877
878
        if (address.startsWith(HelpViewer::NsNokia)
            || address.startsWith(HelpViewer::NsTrolltech)) {
                // local help not installed, resort to external web help
                QString urlPrefix = QLatin1String("http://doc.trolltech.com/");
                if (url.authority() == QLatin1String("com.nokia.qtcreator")) {
879
                    urlPrefix.append(QString::fromLatin1("qtcreator"));
kh1's avatar
kh1 committed
880
881
882
883
                } else {
                    urlPrefix.append(QLatin1String("latest"));
                }
            address = urlPrefix + address.mid(address.lastIndexOf(QLatin1Char('/')));
884
        }
885
886
887
888
889
890
891
892
893
    }

    const QUrl newUrl(address);
    if (newUrl.queryItemValue(QLatin1String("view")) == QLatin1String("split")) {
        if (HelpViewer* viewer = viewerForContextMode())
            viewer->setSource(newUrl);
    } else {
        activateHelpMode();
        m_centralWidget->setSource(newUrl);
894
895
    }
}
con's avatar
con committed
896
897

Q_EXPORT_PLUGIN(HelpPlugin)