Skip to content
Snippets Groups Projects
helpplugin.cpp 44.9 KiB
Newer Older
/**************************************************************************
con's avatar
con committed
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
**
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
**
** Commercial Usage
** 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.
** GNU Lesser General Public License Usage
** 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.
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
**
**************************************************************************/
con's avatar
con committed
#include "helpplugin.h"

#include "bookmarkmanager.h"
#include "centralwidget.h"
#include "contentwindow.h"
con's avatar
con committed
#include "docsettingspage.h"
#include "externalhelpwindow.h"
con's avatar
con committed
#include "filtersettingspage.h"
#include "generalsettingspage.h"
#include "helpconstants.h"
#include "helpfindsupport.h"
con's avatar
con committed
#include "helpindexfilter.h"
#include "helpmanager.h"
con's avatar
con committed
#include "helpmode.h"
#include "helpviewer.h"
#include "indexwindow.h"
#include "openpagesmanager.h"
#include "openpagesmodel.h"
kh1's avatar
kh1 committed
#include "remotehelpfilter.h"
con's avatar
con committed
#include "searchwidget.h"

#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/findplaceholder.h>
#include <coreplugin/icore.h>
kh1's avatar
kh1 committed
#include <coreplugin/helpmanager.h>
hjk's avatar
hjk committed
#include <coreplugin/minisplitter.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/rightpane.h>
#include <coreplugin/sidebar.h>
#include <extensionsystem/pluginmanager.h>
#include <find/findplugin.h>
#include <texteditor/texteditorconstants.h>
#include <utils/styledbar.h>
#include <welcome/welcomemode.h>
con's avatar
con committed
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
#include <QtCore/QLibraryInfo>
#include <QtCore/QTimer>
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
#include <QtCore/QTranslator>
#include <QtCore/qplugin.h>

con's avatar
con committed
#include <QtGui/QAction>
#include <QtGui/QComboBox>
#include <QtGui/QDesktopServices>
#include <QtGui/QMenu>
con's avatar
con committed
#include <QtGui/QShortcut>
#include <QtGui/QStackedLayout>
con's avatar
con committed
#include <QtGui/QSplitter>
#include <QtGui/QToolBar>
con's avatar
con committed
#include <QtHelp/QHelpEngine>

#if !defined(QT_NO_WEBKIT)
#include <QtWebKit/QWebElement>
#include <QtWebKit/QWebElementCollection>
#include <QtWebKit/QWebFrame>
#include <QtWebKit/QWebHistory>
using namespace Core::Constants;
con's avatar
con committed
using namespace Help::Internal;

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");

const char * const SB_OPENPAGES = "OpenPages";

#define IMAGEPATH ":/help/images/"
#if defined(Q_OS_MAC)
#   define DOCPATH "/../Resources/doc/"
#else
#   define DOCPATH "/../share/doc/qtcreator/"
HelpPlugin::HelpPlugin()
    : m_mode(0),
    m_core(0),
con's avatar
con committed
    m_centralWidget(0),
    m_helpViewerForSideBar(0),
    m_contentItem(0),
    m_indexItem(0),
    m_searchItem(0),
    m_bookmarkItem(0),
    m_sideBar(0),
    m_firstModeChange(true),
    m_oldMode(0),
    m_connectWindow(true),
    m_externalWindow(0),
    m_backMenu(0),
    m_nextMenu(0)
con's avatar
con committed
{
}

HelpPlugin::~HelpPlugin()
{
}

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

    const QString &locale = qApp->property("qtc_locale").toString();
Oswald Buddenhagen's avatar
Oswald Buddenhagen committed
    if (!locale.isEmpty()) {
        QTranslator *qtr = new QTranslator(this);
        QTranslator *qhelptr = new QTranslator(this);
        const QString &creatorTrPath = Core::ICore::instance()->resourcePath()
            + QLatin1String("/translations");
        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);
kh1's avatar
kh1 committed
    addAutoReleasedObject(m_helpManager = new LocalHelpManager(this));
    addAutoReleasedObject(m_openPagesManager = new OpenPagesManager(this));
    addAutoReleasedObject(m_docSettingsPage = new DocSettingsPage());
    addAutoReleasedObject(m_filterSettingsPage = new FilterSettingsPage());
    addAutoReleasedObject(m_generalSettingsPage = new GeneralSettingsPage());
    connect(m_generalSettingsPage, SIGNAL(fontChanged()), this,
        SLOT(fontChanged()));
    connect(m_generalSettingsPage, SIGNAL(contextHelpOptionChanged()), this,
        SLOT(contextHelpOptionChanged()));
    connect(m_generalSettingsPage, SIGNAL(returnOnCloseChanged()), this,
        SLOT(updateCloseButton()));
kh1's avatar
kh1 committed
    connect(Core::HelpManager::instance(), SIGNAL(helpRequested(QUrl)), this,
kh1's avatar
kh1 committed
        SLOT(handleHelpRequest(QUrl)));
    connect(m_filterSettingsPage, SIGNAL(filtersChanged()), this,
        SLOT(setupHelpEngineIfNeeded()));
kh1's avatar
kh1 committed
    connect(Core::HelpManager::instance(), SIGNAL(documentationChanged()), this,
        SLOT(setupHelpEngineIfNeeded()));
    connect(Core::HelpManager::instance(), SIGNAL(collectionFileChanged()), this,
        SLOT(setupHelpEngineIfNeeded()));
con's avatar
con committed

    m_splitter = new Core::MiniSplitter;
    m_centralWidget = new Help::Internal::CentralWidget();
    connect(m_centralWidget, SIGNAL(sourceChanged(QUrl)), this,
        SLOT(updateSideBarSource(QUrl)));
    connect(m_centralWidget, SIGNAL(openFindToolBar()), this,
        SLOT(openFindToolBar()));
con's avatar
con committed

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

    action = new QAction(QIcon(QLatin1String(IMAGEPATH "previous.png")),
        tr("Previous Page"), this);
    cmd = am->registerAction(action, Core::Id("Help.Previous"), modecontext);
    cmd->setDefaultKeySequence(QKeySequence::Back);
    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

    action = new QAction(QIcon(QLatin1String(IMAGEPATH "next.png")), tr("Next Page"),
        this);
    cmd = am->registerAction(action, Core::Id("Help.Next"), modecontext);
    cmd->setDefaultKeySequence(QKeySequence::Forward);
    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

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

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

    action = new QAction(tr(SB_INDEX), this);
    cmd = am->registerAction(action, Core::Id("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, Core::Id("Help.Context"), globalcontext);
    am->actionContainer(M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP);
con's avatar
con committed
    cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F1));
    connect(action, SIGNAL(triggered()), this, SLOT(activateContext()));
con's avatar
con committed

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

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

    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());
    if (Core::ActionContainer *advancedMenu = am->actionContainer(M_EDIT_ADVANCED)) {
        // reuse TextEditor constants to avoid a second pair of menu actions
        action = new QAction(tr("Increase Font Size"), this);
        cmd = am->registerAction(action, TextEditor::Constants::INCREASE_FONT_SIZE,
            modecontext);
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl++")));
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(zoomIn()));
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
        action = new QAction(tr("Decrease Font Size"), this);
        cmd = am->registerAction(action, TextEditor::Constants::DECREASE_FONT_SIZE,
            modecontext);
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+-")));
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(zoomOut()));
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
        action = new QAction(tr("Reset Font Size"), this);
        cmd = am->registerAction(action, TextEditor::Constants::RESET_FONT_SIZE,
        cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+0")));
        connect(action, SIGNAL(triggered()), m_centralWidget, SLOT(resetZoom()));
        advancedMenu->addAction(cmd, Core::Constants::G_EDIT_FONT);
    if (Core::ActionContainer *windowMenu = am->actionContainer(M_WINDOW)) {
        // reuse EditorManager constants to avoid a second pair of menu actions
        action = new QAction(QApplication::translate("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::translate("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
    }

    Aggregation::Aggregate *agg = new Aggregation::Aggregate;
    agg->add(m_centralWidget);
    agg->add(new HelpFindSupport(m_centralWidget));

    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
    QWidget *mainWidget = new QWidget;
    m_splitter->addWidget(mainWidget);
    QVBoxLayout *mainWidgetLayout = new QVBoxLayout(mainWidget);
    mainWidgetLayout->setMargin(0);
    mainWidgetLayout->setSpacing(0);
    mainWidgetLayout->addWidget(toolBarWidget);
    mainWidgetLayout->addWidget(m_centralWidget);

    if (QLayout *layout = m_centralWidget->layout()) {
        layout->setSpacing(0);
        layout->addWidget(new Core::FindToolBarPlaceHolder(m_centralWidget));
    }

    HelpIndexFilter *helpIndexFilter = new HelpIndexFilter();
    addAutoReleasedObject(helpIndexFilter);
    connect(helpIndexFilter, SIGNAL(linkActivated(QUrl)), this,
        SLOT(switchToHelpMode(QUrl)));

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

    QDesktopServices::setUrlHandler("qthelp", this, "handleHelpRequest");
    connect(m_core->modeManager(), SIGNAL(currentModeChanged(Core::IMode*,
        Core::IMode*)), this, SLOT(modeChanged(Core::IMode*, Core::IMode*)));
    m_externalWindow = new ExternalHelpWindow;
    if (contextHelpOption() == Help::Constants::ExternalHelpAlways) {
        m_mode = new HelpMode(new QWidget);
        m_mode->setEnabled(false);
kh1's avatar
kh1 committed
        m_externalHelpBar->setVisible(true);
        m_externalWindow->setCentralWidget(m_splitter);
        QTimer::singleShot(0, this, SLOT(showExternalWindow()));
    } else {
        m_mode = new HelpMode(m_splitter);
kh1's avatar
kh1 committed
        m_internalHelpBar->setVisible(true);
    }
    addAutoReleasedObject(m_mode);
con's avatar
con committed
    return true;
}

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

kh1's avatar
kh1 committed
    Core::HelpManager *helpManager = Core::HelpManager::instance();
    foreach (const QString &ns, helpManager->registeredNamespaces()) {
        if (ns.startsWith(QLatin1String("com.nokia.qtcreator."))
kh1's avatar
kh1 committed
            && ns != nsInternal)
            helpManager->unregisterDocumentation(QStringList() << ns);
    }

    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
    // without a qt development version. TODO: is this still really needed, remove
    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")));
    helpManager->registerDocumentation(filesToRegister);
ExtensionSystem::IPlugin::ShutdownFlag HelpPlugin::aboutToShutdown()
    if (m_sideBar)
        m_sideBar->saveSettings(m_core->settings(), QLatin1String("HelpSideBar"));
    delete m_externalWindow;

    return SynchronousShutdown;
void HelpPlugin::setupUi()
    // side bar widgets and shortcuts
    Core::ActionManager *am = m_core->actionManager();
    Core::Context modecontext(Constants::C_MODE_HELP);

    IndexWindow *indexWindow = new IndexWindow();
    indexWindow->setWindowTitle(tr(SB_INDEX));
    m_indexItem = new Core::SideBarItem(indexWindow, QLatin1String(SB_INDEX));

    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)));

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

    ContentWindow *contentWindow = new ContentWindow();
    contentWindow->setWindowTitle(tr(SB_CONTENTS));
    m_contentItem = new Core::SideBarItem(contentWindow, Core::Id(SB_CONTENTS));
    connect(contentWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));

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

    SearchWidget *searchWidget = new SearchWidget();
    searchWidget->setWindowTitle(tr(SB_SEARCH));
    m_searchItem = new Core::SideBarItem(searchWidget, QLatin1String(SB_SEARCH));
    connect(searchWidget, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSourceFromSearch(QUrl)));
kh1's avatar
kh1 committed
     shortcut = new QShortcut(m_splitter);
     shortcut->setWhatsThis(tr("Activate Search in Help mode"));
     cmd = am->registerShortcut(shortcut, Core::Id("Help.SearchShortcut"),
kh1's avatar
kh1 committed
         modecontext);
     cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Slash));
     connect(shortcut, SIGNAL(activated()), this, SLOT(activateSearch()));
con's avatar
con committed
     shortcutMap.insert(QLatin1String(SB_SEARCH), cmd);
kh1's avatar
kh1 committed
    BookmarkManager *manager = &LocalHelpManager::bookmarkManager();
    BookmarkWidget *bookmarkWidget = new BookmarkWidget(manager, 0, false);
    bookmarkWidget->setWindowTitle(tr(SB_BOOKMARKS));
    m_bookmarkItem = new Core::SideBarItem(bookmarkWidget, QLatin1String(SB_BOOKMARKS));
    connect(bookmarkWidget, SIGNAL(linkActivated(QUrl)), m_centralWidget,
        SLOT(setSource(QUrl)));
kh1's avatar
kh1 committed
     shortcut = new QShortcut(m_splitter);
     shortcut->setWhatsThis(tr("Activate Bookmarks in Help mode"));
     cmd = am->registerShortcut(shortcut, Core::Id("Help.BookmarkShortcut"),
kh1's avatar
kh1 committed
         modecontext);
     cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B));
     connect(shortcut, SIGNAL(activated()), this, SLOT(activateBookmarks()));
con's avatar
con committed
     shortcutMap.insert(QLatin1String(SB_BOOKMARKS), cmd);
    QWidget *openPagesWidget = OpenPagesManager::instance().openPagesWidget();
    openPagesWidget->setWindowTitle(tr("Open Pages"));
    m_openPagesItem = new Core::SideBarItem(openPagesWidget, QLatin1String(SB_OPENPAGES));

    shortcut = new QShortcut(m_splitter);
    shortcut->setWhatsThis(tr("Activate Open Pages in Help mode"));
    cmd = am->registerShortcut(shortcut, Core::Id("Help.PagesShortcut"),
        modecontext);
    cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_O));
    connect(shortcut, SIGNAL(activated()), this, SLOT(activateOpenPages()));
    shortcutMap.insert(QLatin1String(SB_OPENPAGES), cmd);
    QList<Core::SideBarItem*> itemList;
    itemList << m_contentItem << m_indexItem << m_searchItem << m_bookmarkItem
        << m_openPagesItem;
    m_sideBar = new Core::SideBar(itemList, QList<Core::SideBarItem*>()
        << m_contentItem << m_openPagesItem);
    m_sideBar->setShortcutMap(shortcutMap);

    m_splitter->setOpaqueResize(false);
con's avatar
con committed
    m_splitter->insertWidget(0, m_sideBar);
    m_splitter->setStretchFactor(0, 0);
    m_splitter->setStretchFactor(1, 1);
    m_sideBar->readSettings(m_core->settings(), QLatin1String("HelpSideBar"));
    m_splitter->setSizes(QList<int>() << m_sideBar->size().width() << 300);
kh1's avatar
kh1 committed
void HelpPlugin::resetFilter()
{
kh1's avatar
kh1 committed
    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);
    }

    // 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();
        if (!filter.isEmpty())
kh1's avatar
kh1 committed
            engine->removeCustomFilter(filter);
kh1's avatar
kh1 committed
    }

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

    updateFilterComboBox();
    connect(engine, SIGNAL(setupFinished()), this, SLOT(updateFilterComboBox()));
void HelpPlugin::createRightPaneContextViewer()
con's avatar
con committed
{
    if (m_helpViewerForSideBar)
        return;
con's avatar
con committed

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

    QAction *back = new QAction(QIcon(QLatin1String(IMAGEPATH "previous.png")),
        tr("Previous"), this);
    QAction *next = new QAction(QIcon(QLatin1String(IMAGEPATH "next.png")),
con's avatar
con committed

    // Dummy layout to align the close button to the right
    QHBoxLayout *hboxLayout = new QHBoxLayout();
    hboxLayout->setSpacing(0);
    hboxLayout->setMargin(0);

    // left side actions
    QToolBar *rightPaneToolBar = new QToolBar();
    setupNavigationMenus(back, next, rightPaneToolBar);

    rightPaneToolBar->addAction(switchToHelp);
    rightPaneToolBar->addAction(back);
    rightPaneToolBar->addAction(next);

    hboxLayout->addWidget(rightPaneToolBar);
    hboxLayout->addStretch();

    QToolButton *closeButton = new QToolButton();
    closeButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_CLOSE)));
con's avatar
con committed
    connect(closeButton, SIGNAL(clicked()), this, SLOT(slotHideRightPane()));

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

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

    QWidget *rightPaneSideBar = new QWidget;
    rightPaneSideBar->setLayout(rightPaneLayout);
    addAutoReleasedObject(new Core::BaseRightPaneWidget(rightPaneSideBar));

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

    m_helpViewerForSideBar = new HelpViewer(qreal(0.0), rightPaneSideBar);
    connect(m_helpViewerForSideBar, SIGNAL(openFindToolBar()), this,
        SLOT(openFindToolBar()));
#if !defined(QT_NO_WEBKIT)
    m_helpViewerForSideBar->pageAction(QWebPage::OpenLinkInNewWindow)->setVisible(false);
#endif
    rightPaneLayout->addWidget(m_helpViewerForSideBar);
    rightPaneLayout->addWidget(new Core::FindToolBarPlaceHolder(rightPaneSideBar));
    rightPaneSideBar->setFocusProxy(m_helpViewerForSideBar);

    Aggregation::Aggregate *agg = new Aggregation::Aggregate();
    agg->add(m_helpViewerForSideBar);
    agg->add(new HelpViewerFindSupport(m_helpViewerForSideBar));
    m_core->addContextObject(new Core::BaseContext(m_helpViewerForSideBar,
        Core::Context(Constants::C_HELP_SIDEBAR), this));
    QAction *copy = new QAction(this);
    Core::Command *cmd = m_core->actionManager()->registerAction(copy,
        Core::Constants::COPY, Core::Context(Constants::C_HELP_SIDEBAR));
    copy->setText(cmd->action()->text());
    copy->setIcon(cmd->action()->icon());
    connect(copy, SIGNAL(triggered()), m_helpViewerForSideBar, SLOT(copy()));
    
    next->setEnabled(m_helpViewerForSideBar->isForwardAvailable());
    connect(next, SIGNAL(triggered()), m_helpViewerForSideBar, SLOT(forward()));
    connect(m_helpViewerForSideBar, SIGNAL(forwardAvailable(bool)), next,
        SLOT(setEnabled(bool)));
    
    back->setEnabled(m_helpViewerForSideBar->isBackwardAvailable());
    connect(back, SIGNAL(triggered()), m_helpViewerForSideBar, SLOT(backward()));
    connect(m_helpViewerForSideBar, SIGNAL(backwardAvailable(bool)), back,
        SLOT(setEnabled(bool)));
kh1's avatar
kh1 committed

    // 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
}

hjk's avatar
hjk committed
void HelpPlugin::activateHelpMode()
{
    if (contextHelpOption() != Help::Constants::ExternalHelpAlways)
        m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP));
    else
        showExternalWindow();
con's avatar
con committed
void HelpPlugin::switchToHelpMode()
{
    switchToHelpMode(m_helpViewerForSideBar->source());
}

void HelpPlugin::switchToHelpMode(const QUrl &source)
{
hjk's avatar
hjk committed
    activateHelpMode();
con's avatar
con committed
    m_centralWidget->setSource(source);
    m_centralWidget->setFocus();
}

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

void HelpPlugin::showHideSidebar()
{
    m_sideBar->setVisible(!m_sideBar->isVisible());
}

void HelpPlugin::showExternalWindow()
{
    bool firstTime = m_firstModeChange;
kh1's avatar
kh1 committed
    doSetupIfNeeded();
    m_externalWindow->show();
    connectExternalHelpWindow();
    m_externalWindow->activateWindow();
        Core::ICore::instance()->mainWindow()->activateWindow();
void HelpPlugin::modeChanged(Core::IMode *mode, Core::IMode *old)
con's avatar
con committed
{
    if (mode == m_mode) {
        m_oldMode = old;
        qApp->setOverrideCursor(Qt::WaitCursor);
kh1's avatar
kh1 committed
        doSetupIfNeeded();
con's avatar
con committed
        qApp->restoreOverrideCursor();
    }
}

void HelpPlugin::updateSideBarSource()
{
    if (HelpViewer *viewer = m_centralWidget->currentHelpViewer()) {
        const QUrl &url = viewer->source();
        if (url.isValid())
            updateSideBarSource(url);
    }
}

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

void HelpPlugin::updateCloseButton()
    Core::HelpManager *manager = Core::HelpManager::instance();
    const bool closeOnReturn = manager->customValue(QLatin1String("ReturnOnClose"),
        false).toBool();
    m_closeButton->setEnabled((OpenPagesManager::instance().pageCount() > 1)
        | closeOnReturn);
void HelpPlugin::fontChanged()
{
    if (!m_helpViewerForSideBar)
        createRightPaneContextViewer();
kh1's avatar
kh1 committed
    const QHelpEngine &engine = LocalHelpManager::helpEngine();
    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);
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;
}

void HelpPlugin::contextHelpOptionChanged()
{
kh1's avatar
kh1 committed
    doSetupIfNeeded();
    QWidget *modeWidget = m_mode->widget();
    if (modeWidget == m_splitter
        && contextHelpOption() == Help::Constants::ExternalHelpAlways) {
        if (QWidget *widget = m_splitter->parentWidget()) {
            if (QStackedLayout *layout = layoutForWidget(widget, m_splitter)) {
                const int index = layout->indexOf(m_splitter);
                layout->removeWidget(m_splitter);
                m_mode->setWidget(new QWidget);
                layout->insertWidget(index, m_mode->widget());
                m_externalWindow->setCentralWidget(m_splitter);
                m_splitter->show();

                slotHideRightPane();
                m_mode->setEnabled(false);
                m_externalHelpBar->setVisible(true);
                m_internalHelpBar->setVisible(false);
                m_externalWindow->show();
                connectExternalHelpWindow();

                if (m_oldMode && m_mode == m_core->modeManager()->currentMode())
                    m_core->modeManager()->activateMode(m_oldMode->id());
            }
        }
    } else if (modeWidget != m_splitter
        && contextHelpOption() != Help::Constants::ExternalHelpAlways) {
        QStackedLayout *wLayout = layoutForWidget(modeWidget->parentWidget(),
            modeWidget);
        if (wLayout && m_splitter->parentWidget()->layout()) {
            const int index = wLayout->indexOf(modeWidget);
            QWidget *tmp = wLayout->widget(index);
            wLayout->removeWidget(modeWidget);
            delete tmp;

            m_splitter->parentWidget()->layout()->removeWidget(m_splitter);
            m_mode->setWidget(m_splitter);
            wLayout->insertWidget(index, m_splitter);

            m_mode->setEnabled(true);
            m_externalWindow->close();
            m_sideBar->setVisible(true);
            m_internalHelpBar->setVisible(true);
            m_externalHelpBar->setVisible(false);
void HelpPlugin::setupHelpEngineIfNeeded()
{
kh1's avatar
kh1 committed
    m_helpManager->setEngineNeedsUpdate();
    if (Core::ModeManager::instance()->currentMode() == m_mode
        || contextHelpOption() == Help::Constants::ExternalHelpAlways)
        m_helpManager->setupGuiHelpEngine();
}

HelpViewer* HelpPlugin::viewerForContextMode()
con's avatar
con committed
{
    using namespace Core;
    bool showSideBySide = false;
    RightPanePlaceHolder *placeHolder = RightPanePlaceHolder::current();
    switch (contextHelpOption()) {
        case Help::Constants::SideBySideIfPossible: {
            // side by side if possible
            if (IEditor *editor = EditorManager::instance()->currentEditor()) {
                if (!placeHolder || !placeHolder->isVisible()) {
                    if (!editor->widget())
                        break;
                    if (!editor->widget()->isVisible())
                        break;
                    if (editor->widget()->width() < 800)
        }   // fall through
        case Help::Constants::SideBySideAlways: {
            // side by side
            showSideBySide = true;
        }   break;
        default: // help mode
            break;
    HelpViewer *viewer = m_centralWidget->currentHelpViewer();
        RightPaneWidget::instance()->setShown(true);
kh's avatar
kh committed
        viewer = m_helpViewerForSideBar;
con's avatar
con committed
    } else {
        activateHelpMode();
            viewer = OpenPagesManager::instance().createPage();
kh's avatar
kh committed
    }
    using namespace Core;
    createRightPaneContextViewer();
    RightPanePlaceHolder* placeHolder = RightPanePlaceHolder::current();
    if (placeHolder && m_helpViewerForSideBar->hasFocus()) {
        switchToHelpMode();
        return;
    } else if (m_core->modeManager()->currentMode() == m_mode)
        return;

    // Find out what to show
    QMap<QString, QUrl> links;
    if (IContext *context = m_core->currentContextObject()) {
        m_idFromContext = context->contextHelpId();
        links = Core::HelpManager::instance()->linksForIdentifier(m_idFromContext);
    if (HelpViewer* viewer = viewerForContextMode()) {
kh's avatar
kh committed
        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 "
                "available.</center></body></html>").arg(m_idFromContext));
con's avatar
con committed
            viewer->setSource(QUrl());
kh's avatar
kh committed
        } else {
            const QUrl &source = *links.begin();
            const QUrl &oldSource = viewer->source();
            if (source != oldSource) {
kh1's avatar
kh1 committed
#if !defined(QT_NO_WEBKIT)
                viewer->stop();
kh1's avatar
kh1 committed
#endif
kh's avatar
kh committed
                viewer->setSource(source);
                connect(viewer, SIGNAL(loadFinished(bool)), this,
                    SLOT(highlightSearchTerms()));
                if (source.toString().remove(source.fragment())
                    == oldSource.toString().remove(oldSource.fragment())) {
                        highlightSearchTerms();
                }
            } else {
#if !defined(QT_NO_WEBKIT)
                viewer->page()->mainFrame()->scrollToAnchor(source.fragment());
#endif
con's avatar
con committed
        }
        viewer->setFocus();
con's avatar
con committed
    }
}

void HelpPlugin::activateIndex()
{
hjk's avatar
hjk committed
    activateHelpMode();
con's avatar
con committed
    m_sideBar->activateItem(m_indexItem);
}

void HelpPlugin::activateContents()
{
hjk's avatar
hjk committed
    activateHelpMode();
con's avatar
con committed
    m_sideBar->activateItem(m_contentItem);
}

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

void HelpPlugin::activateOpenPages()
{
    activateHelpMode();
    m_sideBar->activateItem(m_openPagesItem);
}

kh1's avatar
kh1 committed
void HelpPlugin::activateBookmarks()
{
    activateHelpMode();
    m_sideBar->activateItem(m_bookmarkItem);
}

QToolBar *HelpPlugin::createWidgetToolBar()
con's avatar
con committed
{
kh1's avatar
kh1 committed
    QToolBar *toolBar = new QToolBar;
    toolBar->addWidget(OpenPagesManager::instance().openPagesComboBox());
    toolBar->addWidget(new QLabel(tr("Filtered by:")));
con's avatar
con committed
    m_filterComboBox = new QComboBox;
    m_filterComboBox->setMinimumContentsLength(20);
    toolBar->addWidget(m_filterComboBox);
    connect(m_filterComboBox, SIGNAL(activated(QString)), this,
        SLOT(filterDocumentation(QString)));
    connect(m_filterComboBox, SIGNAL(currentIndexChanged(int)), this,
        SLOT(updateSideBarSource()));

    QWidget *dummy = new QWidget;
    QHBoxLayout *layout = new QHBoxLayout(dummy);
    layout->addStretch();
    toolBar->addWidget(dummy);

    m_closeButton = new QToolButton();
    m_closeButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_CLOSE)));
    m_closeButton->setToolTip(tr("Close current page"));
    connect(m_closeButton, SIGNAL(clicked()), &OpenPagesManager::instance(),
        SLOT(closeCurrentPage()));
    connect(&OpenPagesManager::instance(), SIGNAL(pagesChanged()), this,
        SLOT(updateCloseButton()));
    toolBar->addWidget(m_closeButton);

    return toolBar;
}

QToolBar *HelpPlugin::createIconToolBar(bool external)
{
    QToolBar *toolBar = new QToolBar;

    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 {
        Core::ActionManager *am = m_core->actionManager();
        home = am->command(Core::Id("Help.Home"))->action();
        back = am->command(Core::Id("Help.Previous"))->action();
        next = am->command(Core::Id("Help.Next"))->action();
        bookmark = am->command(Core::Id("Help.AddBookmark"))->action();
    }

    setupNavigationMenus(back, next, toolBar);

    toolBar->addAction(home);
    toolBar->addAction(back);
    toolBar->addAction(next);
    toolBar->addSeparator();
    toolBar->addAction(bookmark);
    toolBar->setMovable(false);
    toolBar->addSeparator();
kh1's avatar
kh1 committed
    toolBar->setVisible(false);
con's avatar
con committed

kh1's avatar
kh1 committed
    return toolBar;
con's avatar
con committed
}

void HelpPlugin::updateFilterComboBox()
{
kh1's avatar
kh1 committed
    const QHelpEngine &engine = LocalHelpManager::helpEngine();
con's avatar
con committed
    QString curFilter = m_filterComboBox->currentText();
    if (curFilter.isEmpty())
        curFilter = engine.currentFilter();
con's avatar
con committed
    m_filterComboBox->clear();
    m_filterComboBox->addItems(engine.customFilters());
con's avatar
con committed
    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
    LocalHelpManager::helpEngine().setCurrentFilter(customFilter);
con's avatar
con committed
}

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

    const QString &url = viewer->source().toString();
    if (url.isEmpty() || url == Help::Constants::AboutBlank)