diff options
Diffstat (limited to 'src/plugins/help')
40 files changed, 4739 insertions, 0 deletions
diff --git a/src/plugins/help/Help.pluginspec b/src/plugins/help/Help.pluginspec new file mode 100644 index 0000000000..b60b320827 --- /dev/null +++ b/src/plugins/help/Help.pluginspec @@ -0,0 +1,12 @@ +<plugin name="Help" version="0.9.1" compatVersion="0.9.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Help system.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="Core" version="0.9.1"/> + <dependency name="Find" version="0.9.1"/> + <dependency name="QuickOpen" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/help/centralwidget.cpp b/src/plugins/help/centralwidget.cpp new file mode 100644 index 0000000000..6c63c28b1e --- /dev/null +++ b/src/plugins/help/centralwidget.cpp @@ -0,0 +1,687 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "centralwidget.h" +#include "helpviewer.h" +#include "topicchooser.h" + +#include <QtCore/QDir> +#include <QtCore/QEvent> +#include <QtCore/QTimer> + +#include <QtGui/QMenu> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QPrinter> +#include <QtGui/QLineEdit> +#include <QtGui/QCheckBox> +#include <QtGui/QTabBar> +#include <QtGui/QTabWidget> +#include <QtGui/QToolButton> +#include <QtGui/QMouseEvent> +#include <QtGui/QFocusEvent> +#include <QtGui/QMainWindow> +#include <QtGui/QSpacerItem> +#include <QtGui/QTextCursor> +#include <QtGui/QPrintDialog> +#include <QtGui/QApplication> +#include <QtGui/QTextDocumentFragment> +#include <QtGui/QPrintPreviewDialog> +#include <QtGui/QPageSetupDialog> + +#include <QtHelp/QHelpEngine> + +using namespace Help::Internal; + +namespace { + HelpViewer* helpViewerFromTabPosition(const QTabWidget *widget, const QPoint &point) + { + QTabBar *tabBar = qFindChild<QTabBar*>(widget); + for (int i = 0; i < tabBar->count(); ++i) { + if (tabBar->tabRect(i).contains(point)) + return qobject_cast<HelpViewer*>(widget->widget(i)); + } + return 0; + } + CentralWidget *staticCentralWidget = 0; +} + +CentralWidget::CentralWidget(QHelpEngine *engine, QWidget *parent) + : QWidget(parent) + , findBar(0) + , tabWidget(0) + , helpEngine(engine) + , printer(0) +{ + lastTabPage = 0; + globalActionList.clear(); + collectionFile = helpEngine->collectionFile(); + + QString system = QLatin1String("win"); + +#ifdef Q_OS_MAC + system = QLatin1String("mac"); +#endif + + tabWidget = new QTabWidget; + tabWidget->setDocumentMode(true); + tabWidget->setMovable(true); + connect(tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); + connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(currentPageChanged(int))); + + QToolButton *newTabButton = new QToolButton(this); + newTabButton->setAutoRaise(true); + newTabButton->setToolTip(tr("Add new page")); + newTabButton->setIcon(QIcon(QString::fromUtf8(":/trolltech/assistant/images/%1/addtab.png").arg(system))); + + tabWidget->setCornerWidget(newTabButton, Qt::TopLeftCorner); + connect(newTabButton, SIGNAL(clicked()), this, SLOT(newTab())); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + vboxLayout->setMargin(0); + vboxLayout->addWidget(tabWidget); + + QTabBar *tabBar = qFindChild<QTabBar*>(tabWidget); + if (tabBar) { + tabBar->installEventFilter(this); + tabBar->setContextMenuPolicy(Qt::CustomContextMenu); + connect(tabBar, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(showTabBarContextMenu(const QPoint&))); + } + + staticCentralWidget = this; +} + +CentralWidget::~CentralWidget() +{ + QDir dir; + QString currentPages; + QHelpEngineCore engine(collectionFile, 0); + + if (engine.setupData()) { + for (int i = 0; i < tabWidget->count(); ++i) { + HelpViewer *viewer = qobject_cast<HelpViewer*>(tabWidget->widget(i)); + if (viewer && viewer->source().isValid()) + currentPages.append(viewer->source().toString()).append(QLatin1Char('|')); + } + engine.setCustomValue(QLatin1String("LastTabPage"), lastTabPage); + engine.setCustomValue(QLatin1String("LastShownPages"), currentPages); + } +} + +CentralWidget *CentralWidget::instance() +{ + return staticCentralWidget; +} + +void CentralWidget::newTab() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + setSourceInNewTab(viewer->source()); +} + +void CentralWidget::zoomIn() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->zoomIn(); +} + +void CentralWidget::zoomOut() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->zoomOut(); +} + +void CentralWidget::nextPage() +{ + if (tabWidget->currentIndex() < tabWidget->count() -1) + tabWidget->setCurrentIndex(tabWidget->currentIndex() +1); + else + tabWidget->setCurrentIndex(0); +} + +void CentralWidget::resetZoom() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->resetZoom(); +} + +void CentralWidget::previousPage() +{ + int index = tabWidget->currentIndex() -1; + if (index >= 0) + tabWidget->setCurrentIndex(index); + else + tabWidget->setCurrentIndex(tabWidget->count() -1); +} + +void CentralWidget::closeTab() +{ + closeTab(tabWidget->currentIndex()); +} + +void CentralWidget::closeTab(int index) +{ + HelpViewer* viewer = helpViewerAtIndex(index); + if (!viewer || tabWidget->count() == 1) + return; + + tabWidget->removeTab(index); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); +} + +void CentralWidget::setSource(const QUrl &url) +{ + HelpViewer* viewer = currentHelpViewer(); + HelpViewer* lastViewer = qobject_cast<HelpViewer*>(tabWidget->widget(lastTabPage)); + + if (!viewer && !lastViewer) { + viewer = new HelpViewer(helpEngine, this); + viewer->installEventFilter(this); + lastTabPage = tabWidget->addTab(viewer, QString()); + tabWidget->setCurrentIndex(lastTabPage); + connectSignals(); + qApp->processEvents(); + } else + viewer = lastViewer; + + viewer->setSource(url); + currentPageChanged(lastTabPage); + viewer->setFocus(Qt::OtherFocusReason); + tabWidget->setCurrentIndex(lastTabPage); + tabWidget->setTabText(lastTabPage, quoteTabTitle(viewer->documentTitle())); +} + +void CentralWidget::setLastShownPages() +{ + const QStringList lastShownPageList = helpEngine->customValue(QLatin1String("LastShownPages")). + toString().split(QLatin1Char('|'), QString::SkipEmptyParts); + + if (!lastShownPageList.isEmpty()) { + foreach (const QString page, lastShownPageList) + setSourceInNewTab(page); + + tabWidget->setCurrentIndex(helpEngine->customValue(QLatin1String("LastTabPage"), 0).toInt()); + } else { + QUrl url = helpEngine->findFile(QUrl("qthelp://com.trolltech.qt.440/qdoc/index.html")); + if (url.isValid()) + setSource(url); + else + setSource(QUrl("qthelp://com.trolltech.qt.440/qdoc/index.html")); + } + + updateBrowserFont(); +} + +bool CentralWidget::hasSelection() const +{ + const HelpViewer* viewer = currentHelpViewer(); + return viewer ? viewer->hasSelection() : false; +} + +QUrl CentralWidget::currentSource() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->source(); + + return QUrl(); +} + +QString CentralWidget::currentTitle() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->documentTitle(); + + return QString(); +} + +void CentralWidget::copySelection() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->copy(); +} + +void CentralWidget::initPrinter() +{ +#ifndef QT_NO_PRINTER + if (!printer) + printer = new QPrinter(QPrinter::HighResolution); +#endif +} + +void CentralWidget::print() +{ +#ifndef QT_NO_PRINTER + HelpViewer* viewer = currentHelpViewer(); + if (!viewer) + return; + + initPrinter(); + + QPrintDialog *dlg = new QPrintDialog(printer, this); +#if !defined(USE_WEBKIT) + if (viewer->textCursor().hasSelection()) + dlg->addEnabledOption(QAbstractPrintDialog::PrintSelection); +#endif + dlg->addEnabledOption(QAbstractPrintDialog::PrintPageRange); + dlg->addEnabledOption(QAbstractPrintDialog::PrintCollateCopies); + dlg->setWindowTitle(tr("Print Document")); + if (dlg->exec() == QDialog::Accepted) { + viewer->print(printer); + } + delete dlg; +#endif +} + +void CentralWidget::printPreview() +{ +#ifndef QT_NO_PRINTER + initPrinter(); + QPrintPreviewDialog preview(printer, this); + connect(&preview, SIGNAL(paintRequested(QPrinter *)), SLOT(printPreview(QPrinter *))); + preview.exec(); +#endif +} + +void CentralWidget::printPreview(QPrinter *p) +{ +#ifndef QT_NO_PRINTER + HelpViewer *viewer = currentHelpViewer(); + if (viewer) + viewer->print(p); +#endif +} + +void CentralWidget::pageSetup() +{ +#ifndef QT_NO_PRINTER + initPrinter(); + QPageSetupDialog dlg(printer); + dlg.exec(); +#endif +} + +bool CentralWidget::isHomeAvailable() const +{ + return currentHelpViewer() ? true : false; +} + +void CentralWidget::home() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->home(); +} + +bool CentralWidget::isForwardAvailable() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->isForwardAvailable(); + + return false; +} + +void CentralWidget::forward() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->forward(); +} + +bool CentralWidget::isBackwardAvailable() const +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) + return viewer->isBackwardAvailable(); + + return false; +} + +void CentralWidget::backward() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->backward(); +} + + +QList<QAction*> CentralWidget::globalActions() const +{ + return globalActionList; +} + +void CentralWidget::setGlobalActions(const QList<QAction*> &actions) +{ + globalActionList = actions; +} +void CentralWidget::setSourceInNewTab(const QUrl &url) +{ + HelpViewer* viewer = new HelpViewer(helpEngine, this); + viewer->installEventFilter(this); + viewer->setSource(url); + viewer->setFocus(Qt::OtherFocusReason); + tabWidget->setCurrentIndex(tabWidget->addTab(viewer, viewer->documentTitle())); + + QFont font = qApp->font(); + if (helpEngine->customValue(QLatin1String("useBrowserFont")).toBool()) + font = qVariantValue<QFont>(helpEngine->customValue(QLatin1String("browserFont"))); + + viewer->setFont(font); + + connectSignals(); +} + +HelpViewer *CentralWidget::newEmptyTab() +{ + HelpViewer* viewer = new HelpViewer(helpEngine, this); + viewer->installEventFilter(this); + viewer->setFocus(Qt::OtherFocusReason); +#if !defined(USE_WEBKIT) + viewer->setDocumentTitle(tr("unknown")); +#endif + tabWidget->setCurrentIndex(tabWidget->addTab(viewer, tr("unknown"))); + + connectSignals(); + return viewer; +} + +void CentralWidget::connectSignals() +{ + const HelpViewer* viewer = currentHelpViewer(); + if (viewer) { + connect(viewer, SIGNAL(copyAvailable(bool)), this, SIGNAL(copyAvailable(bool))); + connect(viewer, SIGNAL(forwardAvailable(bool)), this, SIGNAL(forwardAvailable(bool))); + connect(viewer, SIGNAL(backwardAvailable(bool)), this, SIGNAL(backwardAvailable(bool))); + connect(viewer, SIGNAL(sourceChanged(const QUrl&)), this, SIGNAL(sourceChanged(const QUrl&))); + connect(viewer, SIGNAL(highlighted(const QString&)), this, SIGNAL(highlighted(const QString&))); + + connect(viewer, SIGNAL(sourceChanged(const QUrl&)), this, SLOT(setTabTitle(const QUrl&))); + } +} + +HelpViewer *CentralWidget::helpViewerAtIndex(int index) const +{ + return qobject_cast<HelpViewer*>(tabWidget->widget(index)); +} + +HelpViewer *CentralWidget::currentHelpViewer() const +{ + return qobject_cast<HelpViewer*>(tabWidget->currentWidget()); +} + +void CentralWidget::activateTab(bool onlyHelpViewer) +{ + if (currentHelpViewer()) { + currentHelpViewer()->setFocus(); + } else { + int idx = 0; + if (onlyHelpViewer) + idx = lastTabPage; + tabWidget->setCurrentIndex(idx); + tabWidget->currentWidget()->setFocus(); + } +} + +void CentralWidget::setTabTitle(const QUrl& url) +{ + int tab = lastTabPage; + HelpViewer* viewer = currentHelpViewer(); + +#if defined(USE_WEBKIT) + if (!viewer || viewer->source() != url) { + QTabBar *tabBar = qFindChild<QTabBar*>(tabWidget); + for (tab = 0; tab < tabBar->count(); ++tab) { + viewer = qobject_cast<HelpViewer*>(tabWidget->widget(tab)); + if (viewer && viewer->source() == url) + break; + } + } +#endif + + if (viewer) { + tabWidget->setTabText(tab, + quoteTabTitle(viewer->documentTitle().trimmed())); + } +} + +void CentralWidget::currentPageChanged(int index) +{ + const HelpViewer *viewer = currentHelpViewer(); + + if (viewer || tabWidget->count() == 1) + lastTabPage = index; + + bool enabled = false; + if (viewer) + enabled = tabWidget->count() > 1; + + tabWidget->setTabsClosable(enabled); + tabWidget->cornerWidget(Qt::TopLeftCorner)->setEnabled(true); + + emit currentViewerChanged(); +} + +void CentralWidget::showTabBarContextMenu(const QPoint &point) +{ + HelpViewer* viewer = helpViewerFromTabPosition(tabWidget, point); + if (!viewer) + return; + + QTabBar *tabBar = qFindChild<QTabBar*>(tabWidget); + + QMenu menu(QLatin1String(""), tabBar); + QAction *new_page = menu.addAction(tr("Add New Page")); + QAction *close_page = menu.addAction(tr("Close This Page")); + QAction *close_pages = menu.addAction(tr("Close Other Pages")); + menu.addSeparator(); + QAction *newBookmark = menu.addAction(tr("Add Bookmark for this Page...")); + + if (tabBar->count() == 1) { + close_page->setEnabled(false); + close_pages->setEnabled(false); + } + + QAction *picked_action = menu.exec(tabBar->mapToGlobal(point)); + if (!picked_action) + return; + + if (picked_action == new_page) + setSourceInNewTab(viewer->source()); + + if (picked_action == close_page) { + tabWidget->removeTab(tabWidget->indexOf(viewer)); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); + } + + if (picked_action == close_pages) { + int currentPage = tabWidget->indexOf(viewer); + for (int i = tabBar->count() -1; i >= 0; --i) { + viewer = qobject_cast<HelpViewer*>(tabWidget->widget(i)); + if (i != currentPage && viewer) { + tabWidget->removeTab(i); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); + + if (i < currentPage) + --currentPage; + } + } + } + + if (picked_action == newBookmark) + emit addNewBookmark(viewer->documentTitle(), viewer->source().toString()); +} + +// if we have a current help viewer then this is the 'focus proxy', otherwise +// it's the tab widget itself +// this is needed, so an embedding program can just set the focus to the central widget +// and it does TheRightThing +void CentralWidget::focusInEvent(QFocusEvent * /* event */) +{ + if (currentHelpViewer()) + QTimer::singleShot(1, currentHelpViewer(), SLOT(setFocus())); + else + QTimer::singleShot(1, tabWidget, SLOT(setFocus())); +} + +bool CentralWidget::eventFilter(QObject *object, QEvent *e) +{ + if (currentHelpViewer() == object && e->type() == QEvent::KeyPress){ + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + if (ke->key() == Qt::Key_Backspace) { + HelpViewer *viewer = currentHelpViewer(); + if (viewer && viewer->isBackwardAvailable()) + viewer->backward(); + return true; + } + } + + QTabBar *tabBar = qobject_cast<QTabBar*>(object); + bool mousRel = e->type() == QEvent::MouseButtonRelease; + bool dblClick = e->type() == QEvent::MouseButtonDblClick; + + if (tabBar && (mousRel || dblClick)) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(e); + HelpViewer *viewer = helpViewerFromTabPosition(tabWidget, mouseEvent->pos()); + if (tabWidget->count() <= 1) + return QWidget::eventFilter(object, e); + + if (viewer && (mouseEvent->button() == Qt::MidButton || dblClick)) { + tabWidget->removeTab(tabWidget->indexOf(viewer)); + QTimer::singleShot(0, viewer, SLOT(deleteLater())); + currentPageChanged(tabWidget->currentIndex()); + return true; + } + } + return QWidget::eventFilter(object, e); +} + +void CentralWidget::updateBrowserFont() +{ + QFont font = qApp->font(); + if (helpEngine->customValue(QLatin1String("useBrowserFont")).toBool()) + font = qVariantValue<QFont>(helpEngine->customValue(QLatin1String("browserFont"))); + + QWidget* widget = 0; + for (int i = 0; i < tabWidget->count(); ++i) { + widget = tabWidget->widget(i); + if (widget->font() != font) + widget->setFont(font); + } +} + +bool CentralWidget::find(const QString &txt, QTextDocument::FindFlags findFlags, bool incremental) +{ + HelpViewer* viewer = currentHelpViewer(); + +#if defined(USE_WEBKIT) + Q_UNUSED(incremental); + if (viewer) { + QWebPage::FindFlags options = QWebPage::FindWrapsAroundDocument; + if (findFlags & QTextDocument::FindBackward) + options |= QWebPage::FindBackward; + if (findFlags & QTextDocument::FindCaseSensitively) + options |= QWebPage::FindCaseSensitively; + + return viewer->findText(txt, options); + } + return false; +#else + QTextCursor cursor; + QTextDocument *doc = 0; + QTextBrowser *browser = 0; + + if (viewer) { + doc = viewer->document(); + cursor = viewer->textCursor(); + browser = qobject_cast<QTextBrowser*>(viewer); + } + /* + if (tabWidget->currentWidget() == m_searchWidget) { + QTextBrowser* browser = qFindChild<QTextBrowser*>(m_searchWidget); + if (browser) { + doc = browser->document(); + cursor = browser->textCursor(); + } + } + */ + if (!browser || !doc || cursor.isNull()) + return false; + if (incremental) + cursor.setPosition(cursor.selectionStart()); + + QTextCursor found = doc->find(txt, cursor, findFlags); + if (found.isNull()) { + if ((findFlags&QTextDocument::FindBackward) == 0) + cursor.movePosition(QTextCursor::Start); + else + cursor.movePosition(QTextCursor::End); + found = doc->find(txt, cursor, findFlags); + if (found.isNull()) { + return false; + } + } + if (!found.isNull()) { + viewer->setTextCursor(found); + } + return true; +#endif +} + +void CentralWidget::showTopicChooser(const QMap<QString, QUrl> &links, + const QString &keyword) +{ + TopicChooser tc(this, keyword, links); + if (tc.exec() == QDialog::Accepted) + setSource(tc.link()); +} + +void CentralWidget::copy() +{ + HelpViewer* viewer = currentHelpViewer(); + if (viewer) + viewer->copy(); +} + +QString CentralWidget::quoteTabTitle(const QString &title) const +{ + QString s = title; + return s.replace(QLatin1Char('&'), QLatin1String("&&")); +} diff --git a/src/plugins/help/centralwidget.h b/src/plugins/help/centralwidget.h new file mode 100644 index 0000000000..a75e999b07 --- /dev/null +++ b/src/plugins/help/centralwidget.h @@ -0,0 +1,157 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef CENTRALWIDGET_H +#define CENTRALWIDGET_H + +#include <QtCore/QUrl> +#include <QtCore/QPoint> +#include <QtCore/QObject> + +#include <QtGui/QWidget> +#include <QtGui/QTextDocument> + +QT_BEGIN_NAMESPACE + +class QEvent; +class QLabel; +class QAction; +class QCheckBox; +class QLineEdit; +class QToolButton; +class QTabWidget; +class QHelpEngine; +class QFocusEvent; +class CentralWidget; +class HelpViewer; + +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class PrintHelper; + +} +} + +QT_BEGIN_NAMESPACE + +class CentralWidget : public QWidget +{ + Q_OBJECT + +public: + CentralWidget(QHelpEngine *engine, QWidget *parent = 0); + ~CentralWidget(); + + bool hasSelection() const; + QUrl currentSource() const; + QString currentTitle() const; + bool isHomeAvailable() const; + bool isForwardAvailable() const; + bool isBackwardAvailable() const; + QList<QAction*> globalActions() const; + void setGlobalActions(const QList<QAction*> &actions); + HelpViewer *currentHelpViewer() const; + void activateTab(bool onlyHelpViewer = false); + bool find(const QString &txt, QTextDocument::FindFlags findFlags, bool incremental); + void setLastShownPages(); + + static CentralWidget *instance(); + +public slots: + void zoomIn(); + void zoomOut(); + void nextPage(); + void resetZoom(); + void previousPage(); + void copySelection(); + void print(); + void pageSetup(); + void printPreview(); + void updateBrowserFont(); + void setSource(const QUrl &url); + void setSourceInNewTab(const QUrl &url); + HelpViewer *newEmptyTab(); + void home(); + void forward(); + void backward(); + void showTopicChooser(const QMap<QString, QUrl> &links, + const QString &keyword); + void copy(); + +protected: + void focusInEvent(QFocusEvent *event); + +signals: + void currentViewerChanged(); + void copyAvailable(bool yes); + void sourceChanged(const QUrl &url); + void highlighted(const QString &link); + void forwardAvailable(bool available); + void backwardAvailable(bool available); + void addNewBookmark(const QString &title, const QString &url); + +private slots: + void newTab(); + void closeTab(); + void closeTab(int index); + void setTabTitle(const QUrl& url); + void currentPageChanged(int index); + void showTabBarContextMenu(const QPoint &point); + void printPreview(QPrinter *printer); + +private: + void connectSignals(); + bool eventFilter(QObject *object, QEvent *e); + void initPrinter(); + HelpViewer *helpViewerAtIndex(int index) const; + QString quoteTabTitle(const QString &title) const; + +private: + int lastTabPage; + QString collectionFile; + QList<QAction*> globalActionList; + + QWidget *findBar; + QTabWidget* tabWidget; + QHelpEngine *helpEngine; + QPrinter *printer; +}; + +QT_END_NAMESPACE +//} // namespace Internal +//} // namespace Help + +#endif // CENTRALWIDGET_H diff --git a/src/plugins/help/contentstoolwindow.cpp b/src/plugins/help/contentstoolwindow.cpp new file mode 100644 index 0000000000..1af3aff3b1 --- /dev/null +++ b/src/plugins/help/contentstoolwindow.cpp @@ -0,0 +1,180 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtCore/QStack> +#include <QtGui/QFocusEvent> +#include <QtGui/QKeyEvent> + +#include "contentstoolwindow.h" +#include "helpengine.h" + +using namespace Help::Internal; + +ContentsToolWidget::ContentsToolWidget() +{ + wasInitialized = false; + + setRootIsDecorated(true); + setItemHidden(headerItem(), true); + setUniformRowHeights(true); + setColumnCount(1); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + setWindowTitle(tr("Contents")); + setWindowIcon(QIcon(":/help/images/book.png")); +} + +void ContentsToolWidget::focusInEvent(QFocusEvent *e) +{ + if (wasInitialized) { + if (e && e->reason() != Qt::MouseFocusReason + && !currentItem() && topLevelItemCount()) + setCurrentItem(topLevelItem(0)); + return; + } + wasInitialized = true; + setCursor(QCursor(Qt::WaitCursor)); + emit buildRequested(); +} + +void ContentsToolWidget::showEvent(QShowEvent *) +{ + if (wasInitialized) + return; + wasInitialized = true; + setCursor(QCursor(Qt::WaitCursor)); + emit buildRequested(); +} + +void ContentsToolWidget::keyPressEvent(QKeyEvent *e) +{ + if (e && e->key() == Qt::Key_Escape) { + emit escapePressed(); + e->accept(); + return; + } + QTreeWidget::keyPressEvent(e); +} + + +enum +{ + LinkRole = Qt::UserRole + 1000 +}; + +ContentsToolWindow::ContentsToolWindow(const QList<int> &context, HelpEngine *help) +{ + m_widget = new ContentsToolWidget; + helpEngine = help; + connect(helpEngine, SIGNAL(contentsInitialized()), this, SLOT(contentsDone())); + connect(m_widget, SIGNAL(buildRequested()), helpEngine, SLOT(buildContents())); + + m_context = context; + m_context << 0; + + connect(m_widget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(indexRequested())); + connect(m_widget, SIGNAL(escapePressed()), this, SIGNAL(escapePressed())); +} + +ContentsToolWindow::~ContentsToolWindow() +{ + delete m_widget; +} + +const QList<int> &ContentsToolWindow::context() const +{ + return m_context; +} + +QWidget *ContentsToolWindow::widget() +{ + return m_widget; +} + +void ContentsToolWindow::contentsDone() +{ + m_widget->setCursor(QCursor(Qt::WaitCursor)); + QList<QPair<QString, ContentList> > contentList = helpEngine->contents(); + for(QList<QPair<QString, ContentList> >::Iterator it = contentList.begin(); it != contentList.end(); ++it) { + QTreeWidgetItem *newEntry; + QTreeWidgetItem *contentEntry; + QStack<QTreeWidgetItem*> stack; + stack.clear(); + int depth = 0; + bool root = false; + + QTreeWidgetItem *lastItem[64]; + for(int j = 0; j < 64; ++j) + lastItem[j] = 0; + + ContentList lst = (*it).second; + for (ContentList::ConstIterator it = lst.begin(); it != lst.end(); ++it) { + ContentItem item = *it; + if (item.depth == 0) { + newEntry = new QTreeWidgetItem(m_widget, 0); + newEntry->setIcon(0, QIcon(QString::fromUtf8(":/help/images/book.png"))); + newEntry->setText(0, item.title); + newEntry->setData(0, LinkRole, item.reference); + stack.push(newEntry); + depth = 1; + root = true; + } + else{ + if((item.depth > depth) && root) { + depth = item.depth; + stack.push(contentEntry); + } + if(item.depth == depth) { + contentEntry = new QTreeWidgetItem(stack.top(), lastItem[ depth ]); + lastItem[ depth ] = contentEntry; + contentEntry->setText(0, item.title); + contentEntry->setData(0, LinkRole, item.reference); + } + else if(item.depth < depth) { + stack.pop(); + depth--; + item = *(--it); + } + } + } + } + m_widget->setCursor(QCursor(Qt::ArrowCursor)); +} + +void ContentsToolWindow::indexRequested() +{ + QTreeWidgetItem *itm = m_widget->currentItem(); + if (!itm) + return; + emit showLinkRequested(itm->data(0, LinkRole).toString(), false); +} diff --git a/src/plugins/help/contentstoolwindow.h b/src/plugins/help/contentstoolwindow.h new file mode 100644 index 0000000000..51163c1ea1 --- /dev/null +++ b/src/plugins/help/contentstoolwindow.h @@ -0,0 +1,102 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CONTENTSTOOLWINDOW_H +#define CONTENTSTOOLWINDOW_H + +#include <QtGui/QTreeWidget> + +#include <coreplugin/iview.h> + +namespace Help { +namespace Internal { + +class HelpEngine; +class ContentsToolWindow; + +class ContentsToolWidget : public QTreeWidget +{ + Q_OBJECT +public: + ContentsToolWidget(); + +signals: + void buildRequested(); + void escapePressed(); + +private: + friend class ContentsToolWindow; + void showEvent(QShowEvent *e); + void focusInEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + + bool wasInitialized; +}; + +class ContentsToolWindow : public Core::IView +{ + Q_OBJECT + +public: + ContentsToolWindow(const QList<int> &context, HelpEngine *help); + ~ContentsToolWindow(); + + const QList<int> &context() const; + QWidget *widget(); + + QList<QWidget*> dockToolBarWidgets() const { return QList<QWidget*>(); } + + const char *uniqueViewName() const { return "Help.ContentsToolWindow"; } + const char *globalMenuGroup() const { return "Help.Group"; } + inline QKeySequence defaultShortcut() const { return QKeySequence(); } + Qt::DockWidgetArea defaultArea() const { return Qt::RightDockWidgetArea; } + IView::ViewPosition defaultPosition() const { return IView::First; } + +signals: + void showLinkRequested(const QString &link, bool newWindow); + void escapePressed(); + +private slots: + void contentsDone(); + void indexRequested(); + +private: + HelpEngine *helpEngine; + + QList<int> m_context; + ContentsToolWidget *m_widget; +}; + +} //namespace Internal +} //namespace Help + +#endif diff --git a/src/plugins/help/docsettingspage.cpp b/src/plugins/help/docsettingspage.cpp new file mode 100644 index 0000000000..763d134c23 --- /dev/null +++ b/src/plugins/help/docsettingspage.cpp @@ -0,0 +1,143 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "docsettingspage.h" + +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> +#include <QtHelp/QHelpEngine> + +using namespace Help::Internal; + +DocSettingsPage::DocSettingsPage(QHelpEngine *helpEngine) + : m_helpEngine(helpEngine), + m_registeredDocs(false) +{ + +} + +QString DocSettingsPage::name() const +{ + return "Documentation"; +} + +QString DocSettingsPage::category() const +{ + return "Help"; +} + +QString DocSettingsPage::trCategory() const +{ + return tr("Help"); +} + +QWidget *DocSettingsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_ui.setupUi(w); + + connect(m_ui.addButton, SIGNAL(clicked()), + this, SLOT(addDocumentation())); + connect(m_ui.removeButton, SIGNAL(clicked()), + this, SLOT(removeDocumentation())); + + m_ui.docsListWidget->addItems(m_helpEngine->registeredDocumentations()); + m_registeredDocs = false; + m_removeDocs.clear(); + + return w; +} + +void DocSettingsPage::addDocumentation() +{ + QStringList files = QFileDialog::getOpenFileNames(m_ui.addButton->parentWidget(), + tr("Add Documentation"), + QString(), tr("Qt Help Files (*.qch)")); + + if (files.isEmpty()) + return; + + foreach (const QString &file, files) { + QString nsName = QHelpEngineCore::namespaceName(file); + if (nsName.isEmpty()) { + QMessageBox::warning(m_ui.addButton->parentWidget(), + tr("Add Documentation"), + tr("The file %1 is not a valid Qt Help file!") + .arg(file)); + continue; + } + m_helpEngine->registerDocumentation(file); + m_ui.docsListWidget->addItem(nsName); + } + m_registeredDocs = true; + emit documentationAdded(); +} + +void DocSettingsPage::removeDocumentation() +{ + QListWidgetItem *item = m_ui.docsListWidget->currentItem(); + if (!item) + return; + + m_removeDocs.append(item->text()); + int row = m_ui.docsListWidget->currentRow(); + m_ui.docsListWidget->takeItem(row); + if (row > 0) + --row; + if (m_ui.docsListWidget->count()) + m_ui.docsListWidget->setCurrentRow(row); + + delete item; +} + +void DocSettingsPage::finished(bool accepted) +{ + if (!accepted) + return; + + emit dialogAccepted(); +} + +bool DocSettingsPage::applyChanges() +{ + QStringList::const_iterator it = m_removeDocs.constBegin(); + while (it != m_removeDocs.constEnd()) { + if (!m_helpEngine->unregisterDocumentation((*it))) { + QMessageBox::warning(m_ui.addButton->parentWidget(), + tr("Documentation"), + tr("Cannot unregister documentation file %1!") + .arg((*it))); + } + ++it; + } + return m_registeredDocs || m_removeDocs.count(); +} diff --git a/src/plugins/help/docsettingspage.h b/src/plugins/help/docsettingspage.h new file mode 100644 index 0000000000..efafb94034 --- /dev/null +++ b/src/plugins/help/docsettingspage.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef DOCSETTINGSPAGE_H +#define DOCSETTINGSPAGE_H + +#include <QtGui/QWidget> +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_docsettingspage.h" + +QT_FORWARD_DECLARE_CLASS(QHelpEngine) + +namespace Help { +namespace Internal { + +class DocSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + DocSettingsPage(QHelpEngine *helpEngine); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + bool applyChanges(); + +signals: + void documentationAdded(); + void dialogAccepted(); + +private slots: + void addDocumentation(); + void removeDocumentation(); + +private: + QHelpEngine *m_helpEngine; + bool m_registeredDocs; + QStringList m_removeDocs; + Ui::DocSettingsPage m_ui; +}; + +} // namespace Help +} // namespace Internal + +#endif // DOCSETTINGSPAGE_H diff --git a/src/plugins/help/docsettingspage.ui b/src/plugins/help/docsettingspage.ui new file mode 100644 index 0000000000..311be060c7 --- /dev/null +++ b/src/plugins/help/docsettingspage.ui @@ -0,0 +1,77 @@ +<ui version="4.0" > + <class>DocSettingsPage</class> + <widget class="QWidget" name="DocSettingsPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>364</width> + <height>240</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string>Registered Documentation:</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="_3" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QListWidget" name="docsListWidget" /> + </item> + <item> + <layout class="QVBoxLayout" name="_4" > + <property name="spacing" > + <number>6</number> + </property> + <property name="margin" > + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="addButton" > + <property name="text" > + <string>Add...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeButton" > + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/help/filtersettingspage.cpp b/src/plugins/help/filtersettingspage.cpp new file mode 100644 index 0000000000..6087a94084 --- /dev/null +++ b/src/plugins/help/filtersettingspage.cpp @@ -0,0 +1,220 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "filtersettingspage.h" +#include "filternamedialog.h" + +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> +#include <QtHelp/QHelpEngine> + +using namespace Help::Internal; + +FilterSettingsPage::FilterSettingsPage(QHelpEngine *helpEngine) +{ + m_helpEngine = helpEngine; +} + +QString FilterSettingsPage::name() const +{ + return "Filters"; +} + +QString FilterSettingsPage::category() const +{ + return "Help"; +} + +QString FilterSettingsPage::trCategory() const +{ + return tr("Help"); +} + +QWidget *FilterSettingsPage::createPage(QWidget *parent) +{ + m_currentPage = new QWidget(parent); + m_ui.setupUi(m_currentPage); + m_ui.attributeWidget->header()->hide(); + m_ui.attributeWidget->setRootIsDecorated(false); + + connect(m_ui.attributeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), + this, SLOT(updateFilterMap())); + connect(m_ui.filterWidget, + SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), + this, SLOT(updateAttributes(QListWidgetItem*))); + connect(m_ui.filterAddButton, SIGNAL(clicked()), + this, SLOT(addFilter())); + connect(m_ui.filterRemoveButton, SIGNAL(clicked()), + this, SLOT(removeFilter())); + updateFilterPage(); + + return m_currentPage; +} + +void FilterSettingsPage::updateFilterPage() +{ + if (!m_helpEngine) + return; + + m_ui.filterWidget->clear(); + m_ui.attributeWidget->clear(); + + QHelpEngineCore help(m_helpEngine->collectionFile(), 0); + help.setupData(); + m_filterMapBackup.clear(); + const QStringList filters = help.customFilters(); + foreach (const QString filter, filters) { + QStringList atts = help.filterAttributes(filter); + m_filterMapBackup.insert(filter, atts); + if (!m_filterMap.contains(filter)) + m_filterMap.insert(filter, atts); + } + + m_ui.filterWidget->addItems(m_filterMap.keys()); + + foreach (const QString a, help.filterAttributes()) + new QTreeWidgetItem(m_ui.attributeWidget, QStringList() << a); + + if (m_filterMap.keys().count()) + m_ui.filterWidget->setCurrentRow(0); +} + +void FilterSettingsPage::updateAttributes(QListWidgetItem *item) +{ + QStringList checkedList; + if (item) + checkedList = m_filterMap.value(item->text()); + QTreeWidgetItem *itm; + for (int i=0; i<m_ui.attributeWidget->topLevelItemCount(); ++i) { + itm = m_ui.attributeWidget->topLevelItem(i); + if (checkedList.contains(itm->text(0))) + itm->setCheckState(0, Qt::Checked); + else + itm->setCheckState(0, Qt::Unchecked); + } +} + +void FilterSettingsPage::updateFilterMap() +{ + if (!m_ui.filterWidget->currentItem()) + return; + QString filter = m_ui.filterWidget->currentItem()->text(); + if (!m_filterMap.contains(filter)) + return; + + QStringList newAtts; + QTreeWidgetItem *itm = 0; + for (int i=0; i<m_ui.attributeWidget->topLevelItemCount(); ++i) { + itm = m_ui.attributeWidget->topLevelItem(i); + if (itm->checkState(0) == Qt::Checked) + newAtts.append(itm->text(0)); + } + m_filterMap[filter] = newAtts; +} + +void FilterSettingsPage::addFilter() +{ + FilterNameDialog dia(m_currentPage); + if (dia.exec() == QDialog::Rejected) + return; + + QString filterName = dia.filterName(); + if (!m_filterMap.contains(filterName)) { + m_filterMap.insert(filterName, QStringList()); + m_ui.filterWidget->addItem(filterName); + } + + QList<QListWidgetItem*> lst = m_ui.filterWidget + ->findItems(filterName, Qt::MatchCaseSensitive); + m_ui.filterWidget->setCurrentItem(lst.first()); +} + +void FilterSettingsPage::removeFilter() +{ + QListWidgetItem *item = m_ui.filterWidget + ->takeItem(m_ui.filterWidget->currentRow()); + if (!item) + return; + + m_filterMap.remove(item->text()); + m_removedFilters.append(item->text()); + delete item; + if (m_ui.filterWidget->count()) + m_ui.filterWidget->setCurrentRow(0); +} + +void FilterSettingsPage::finished(bool) +{ +} + +bool FilterSettingsPage::applyChanges() +{ + bool changed = false; + if (m_filterMap.count() != m_filterMapBackup.count()) { + changed = true; + } else { + QMapIterator<QString, QStringList> it(m_filterMapBackup); + while (it.hasNext() && !changed) { + it.next(); + if (!m_filterMap.contains(it.key())) { + changed = true; + } else { + QStringList a = it.value(); + QStringList b = m_filterMap.value(it.key()); + if (a.count() != b.count()) { + changed = true; + } else { + QStringList::const_iterator i(a.constBegin()); + while (i != a.constEnd()) { + if (!b.contains(*i)) { + changed = true; + break; + } + ++i; + } + } + } + } + } + if (changed) { + foreach (QString filter, m_removedFilters) + m_helpEngine->removeCustomFilter(filter); + QMapIterator<QString, QStringList> it(m_filterMap); + while (it.hasNext()) { + it.next(); + m_helpEngine->addCustomFilter(it.key(), it.value()); + } + return true; + } + return false; +} diff --git a/src/plugins/help/filtersettingspage.h b/src/plugins/help/filtersettingspage.h new file mode 100644 index 0000000000..5cf83619a2 --- /dev/null +++ b/src/plugins/help/filtersettingspage.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef FILTERSETTINGSPAGE_H +#define FILTERSETTINGSPAGE_H + +#include <QtGui/QWidget> +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_filtersettingspage.h" + +QT_FORWARD_DECLARE_CLASS(QHelpEngine) + +namespace Help { +namespace Internal { + +class FilterSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + FilterSettingsPage(QHelpEngine *helpEngine); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + bool applyChanges(); + +private slots: + void updateAttributes(QListWidgetItem *item); + void updateFilterMap(); + void updateFilterPage(); + void addFilter(); + void removeFilter(); + +private: + QHelpEngine *m_helpEngine; + Ui::FilterSettingsPage m_ui; + QMap<QString, QStringList> m_filterMapBackup; + QMap<QString, QStringList> m_filterMap; + QStringList m_removedFilters; + QWidget *m_currentPage; +}; + +} // namespace Help +} // namespace Internal + +#endif // DOCSETTINGSPAGE_H diff --git a/src/plugins/help/filtersettingspage.ui b/src/plugins/help/filtersettingspage.ui new file mode 100644 index 0000000000..367e7ce0a9 --- /dev/null +++ b/src/plugins/help/filtersettingspage.ui @@ -0,0 +1,72 @@ +<ui version="4.0" > + <class>FilterSettingsPage</class> + <widget class="QWidget" name="FilterSettingsPage" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout" > + <item row="0" column="0" colspan="2" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Filter:</string> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="label_2" > + <property name="frameShape" > + <enum>QFrame::NoFrame</enum> + </property> + <property name="text" > + <string>Attributes:</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2" > + <widget class="QListWidget" name="filterWidget" /> + </item> + <item rowspan="2" row="1" column="2" > + <widget class="QTreeWidget" name="attributeWidget" > + <property name="showDropIndicator" stdset="0" > + <bool>false</bool> + </property> + <property name="rootIsDecorated" > + <bool>false</bool> + </property> + <property name="uniformRowHeights" > + <bool>true</bool> + </property> + <column> + <property name="text" > + <string>1</string> + </property> + </column> + </widget> + </item> + <item row="2" column="0" > + <widget class="QPushButton" name="filterAddButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QPushButton" name="filterRemoveButton" > + <property name="text" > + <string>Remove</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/help/help.pri b/src/plugins/help/help.pri new file mode 100644 index 0000000000..cea3ed71f9 --- /dev/null +++ b/src/plugins/help/help.pri @@ -0,0 +1,3 @@ +include(help_dependencies.pri) + +LIBS *= -l$$qtLibraryTarget(Help) diff --git a/src/plugins/help/help.pro b/src/plugins/help/help.pro new file mode 100644 index 0000000000..a7de200f74 --- /dev/null +++ b/src/plugins/help/help.pro @@ -0,0 +1,36 @@ +TEMPLATE = lib +TARGET = Help +include(../../qworkbenchplugin.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/find/find.pri) +include(../../plugins/quickopen/quickopen.pri) +QT += network +CONFIG += help +DEFINES += QT_CLUCENE_SUPPORT \ + HELP_LIBRARY +HEADERS += helpplugin.h \ + docsettingspage.h \ + filtersettingspage.h \ + helpmode.h \ + centralwidget.h \ + searchwidget.h \ + helpfindsupport.h \ + help_global.h \ + helpindexfilter.h \ + indexwindow.h +SOURCES += helpplugin.cpp \ + docsettingspage.cpp \ + filtersettingspage.cpp \ + helpmode.cpp \ + centralwidget.cpp \ + searchwidget.cpp \ + helpfindsupport.cpp \ + helpindexfilter.cpp +FORMS += docsettingspage.ui \ + filtersettingspage.ui +RESOURCES += help.qrc +include(../../../shared/help/help.pri) +contains(QT_CONFIG, webkit) { + DEFINES += USE_WEBKIT + QT += webkit +} diff --git a/src/plugins/help/help.qrc b/src/plugins/help/help.qrc new file mode 100644 index 0000000000..0b529633f8 --- /dev/null +++ b/src/plugins/help/help.qrc @@ -0,0 +1,16 @@ +<RCC> + <qresource prefix="/help" > + <file>images/find.png</file> + <file>images/book.png</file> + <file>images/previous.png</file> + <file>images/next.png</file> + <file>images/home.png</file> + <file>images/bookmark.png</file> + </qresource> + <qresource prefix="/trolltech/assistant" > + <file>images/mac/addtab.png</file> + <file>images/mac/closetab.png</file> + <file>images/win/addtab.png</file> + <file>images/win/closetab.png</file> + </qresource> +</RCC> diff --git a/src/plugins/help/help_global.h b/src/plugins/help/help_global.h new file mode 100644 index 0000000000..104511ac88 --- /dev/null +++ b/src/plugins/help/help_global.h @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ Trolltech AS. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef HELP_GLOBAL_H +#define HELP_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(HELP_LIBRARY) +# define HELP_EXPORT Q_DECL_EXPORT +#else +# define HELP_EXPORT Q_DECL_IMPORT +#endif + +#endif // HELP_GLOBAL_H diff --git a/src/plugins/help/helpengine.cpp b/src/plugins/help/helpengine.cpp new file mode 100644 index 0000000000..790e4bf54a --- /dev/null +++ b/src/plugins/help/helpengine.cpp @@ -0,0 +1,606 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QDateTime> +#include <QtCore/QCoreApplication> + +#include "helpengine.h" +#include "config.h" + +using namespace Help::Internal; + +static bool verifyDirectory(const QString &str) +{ + QFileInfo dirInfo(str); + if (!dirInfo.exists()) + return QDir().mkdir(str); + if (!dirInfo.isDir()) { + qWarning("'%s' exists but is not a directory", str.toLatin1().constData()); + return false; + } + return true; +} + +struct IndexKeyword { + IndexKeyword(const QString &kw, const QString &l) + : keyword(kw), link(l) {} + IndexKeyword() : keyword(QString()), link(QString()) {} + bool operator<(const IndexKeyword &ik) const { + return keyword.toLower() < ik.keyword.toLower(); + } + bool operator<=(const IndexKeyword &ik) const { + return keyword.toLower() <= ik.keyword.toLower(); + } + bool operator>(const IndexKeyword &ik) const { + return keyword.toLower() > ik.keyword.toLower(); + } + Q_DUMMY_COMPARISON_OPERATOR(IndexKeyword) + QString keyword; + QString link; +}; + +QDataStream &operator>>(QDataStream &s, IndexKeyword &ik) +{ + s >> ik.keyword; + s >> ik.link; + return s; +} + +QDataStream &operator<<(QDataStream &s, const IndexKeyword &ik) +{ + s << ik.keyword; + s << ik.link; + return s; +} + + +/** + * Compare in a human-preferred alphanumeric way, + * e.g. 'Qt tutorial 2' will be less than 'Qt tutorial 11'. + */ +bool caseInsensitiveLessThan(const QString &as, const QString &bs) +{ + const QChar *a = as.unicode(); + const QChar *b = bs.unicode(); + int result = 0; + while (result == 0) + { + ushort aa = a->unicode(); + ushort bb = b->unicode(); + + if (aa == 0 || bb == 0) { + result = aa - bb; + break; + } + else if (a->isDigit() && b->isDigit()) + { + const QChar *a_begin = a; + const QChar *b_begin = b; + bool loop = true; + do { + if (a->isDigit()) ++a; + else if (b->isDigit()) ++b; + else loop = false; + } while (loop); + + // optimization: comparing the length of the two numbers is more efficient than constructing two qstrings. + result = (a - a_begin) - (b - b_begin); + if (result == 0) { + QString astr(a_begin, a - a_begin); + QString bstr(b_begin, b - b_begin); + long la = astr.toLong(); + long lb = bstr.toLong(); + result = la - lb; + } + } else { + aa = QChar(aa).toLower().unicode(); + bb = QChar(bb).toLower().unicode(); + result = aa - bb; + ++a; + ++b; + } + } + + return result < 0 ? true : false; +} + +/** + * \a real is kinda a hack for the smart search, need a way to match a regexp to an item + * How would you say the best match for Q.*Wiget is QWidget? + */ +QModelIndex IndexListModel::filter(const QString &s, const QString &real) +{ + QStringList list; + + int goodMatch = -1; + int perfectMatch = -1; + if (s.isEmpty()) + perfectMatch = 0; + + const QRegExp regExp(s); + QMultiMap<QString, QString>::iterator it = contents.begin(); + QString lastKey; + for (; it != contents.end(); ++it) { + if (it.key() == lastKey) + continue; + lastKey = it.key(); + const QString key = it.key(); + if (key.contains(regExp) || key.contains(s, Qt::CaseInsensitive)) { + list.append(key); + //qDebug() << regExp << regExp.indexIn(s) << s << key << regExp.matchedLength(); + if (perfectMatch == -1 && (key.startsWith(real, Qt::CaseInsensitive))) { + if (goodMatch == -1) + goodMatch = list.count() - 1; + if (s.length() == key.length()) + perfectMatch = list.count() - 1; + } else if (perfectMatch > -1 && s == key) { + perfectMatch = list.count() - 1; + } + } + } + + int bestMatch = perfectMatch; + if (bestMatch == -1) + bestMatch = goodMatch; + + bestMatch = qMax(0, bestMatch); + + // sort the new list + QString match; + if (bestMatch >= 0 && list.count() > bestMatch) + match = list[bestMatch]; + qSort(list.begin(), list.end(), caseInsensitiveLessThan); + setStringList(list); + for (int i = 0; i < list.size(); ++i) { + if (list.at(i) == match){ + bestMatch = i; + break; + } + } + return index(bestMatch, 0, QModelIndex()); +} + + + + + + + + + +HelpEngine::HelpEngine(QObject *parent, const QString &defaultQtVersionPath) + : QObject(parent) +{ + titleMapThread = new TitleMapThread(this); + connect(titleMapThread, SIGNAL(errorOccured(const QString&)), + this, SIGNAL(errorOccured(const QString&))); + connect(titleMapThread, SIGNAL(finished()), this, SLOT(titleMapFinished())); + indexThread = new IndexThread(this); + connect(indexThread, SIGNAL(errorOccured(const QString&)), + this, SIGNAL(errorOccured(const QString&))); + connect(indexThread, SIGNAL(finished()), this, SLOT(indexFinished())); + + indexModel = new IndexListModel(this); + + Config::loadConfig(defaultQtVersionPath); + cacheFilesPath = QDir::homePath() + QLatin1String("/.assistant"); +} + +HelpEngine::~HelpEngine() +{ + Config::configuration()->save(); +} + +void HelpEngine::init() +{ +} + +QString HelpEngine::cacheFilePath() const +{ + return cacheFilesPath; +} + +IndexListModel *HelpEngine::indices() +{ + return indexModel; +} + +void HelpEngine::buildContents() +{ + contentsOnly = true; + if (!titleMapThread->isRunning()) { + titleMapThread->start(QThread::NormalPriority); + } +} + +void HelpEngine::buildIndex() +{ + if (!titleMapThread->isRunning()) { + contentsOnly = false; + titleMapThread->start(QThread::NormalPriority); + } + if (!indexThread->isRunning()) + indexThread->start(QThread::NormalPriority); +} + +void HelpEngine::titleMapFinished() +{ + contentList = titleMapThread->contents(); + titleMap = titleMapThread->documentTitleMap(); + if (contentsOnly) { + contentsOnly = false; + emit contentsInitialized(); + } +} + +void HelpEngine::indexFinished() +{ + indexModel = indexThread->model(); + emit indexInitialized(); +} + +void HelpEngine::removeOldCacheFiles(bool onlyFulltextSearchIndex) +{ + if (!verifyDirectory(cacheFilesPath)) { + qWarning("Failed to created assistant directory"); + return; + } + QString pname = QLatin1String(".") + Config::configuration()->profileName(); + + QStringList fileList; + fileList << QLatin1String("indexdb40.dict") + << QLatin1String("indexdb40.doc"); + + if (!onlyFulltextSearchIndex) + fileList << QLatin1String("indexdb40") << QLatin1String("contentdb40"); + + QStringList::iterator it = fileList.begin(); + for (; it != fileList.end(); ++it) { + if (QFile::exists(cacheFilesPath + QDir::separator() + *it + pname)) { + QFile f(cacheFilesPath + QDir::separator() + *it + pname); + f.remove(); + } + } +} + +quint32 HelpEngine::getFileAges() +{ + QStringList addDocuFiles = Config::configuration()->docFiles(); + QStringList::const_iterator i = addDocuFiles.begin(); + + quint32 fileAges = 0; + for(; i != addDocuFiles.end(); ++i) { + QFileInfo fi(*i); + if (fi.exists()) + fileAges += fi.lastModified().toTime_t(); + } + + return fileAges; +} + +QString HelpEngine::removeAnchorFromLink(const QString &link) +{ + int i = link.length(); + int j = link.lastIndexOf('/'); + int l = link.lastIndexOf(QDir::separator()); + if (l > j) + j = l; + if (j > -1) { + QString fileName = link.mid(j+1); + int k = fileName.lastIndexOf('#'); + if (k > -1) + i = j + k + 1; + } + return link.left(i); +} + +QString HelpEngine::titleOfLink(const QString &link) +{ + QString s = HelpEngine::removeAnchorFromLink(link); + s = titleMap[s]; + if (s.isEmpty()) + return link; + return s; +} + +QString HelpEngine::home() const +{ + QString link = Config::configuration()->homePage(); + if (!link.startsWith(QLatin1String("file:"))) + link.prepend("file:"); + return link; +} + + + + + + + + + + +TitleMapThread::TitleMapThread(HelpEngine *he) + : QThread(he) +{ + engine = he; + done = false; +} + +TitleMapThread::~TitleMapThread() +{ + +} + +void TitleMapThread::run() +{ + if (done) { + engine->mutex.lock(); + engine->titleMapDoneCondition.wakeAll(); + engine->mutex.unlock(); + return; + } + + bool needRebuild = false; + if (Config::configuration()->profileName() == QLatin1String("default")) { + const QStringList docuFiles = Config::configuration()->docFiles(); + for(QStringList::ConstIterator it = docuFiles.begin(); it != docuFiles.end(); it++) { + if (!QFile::exists(*it)) { + Config::configuration()->saveProfile(Profile::createDefaultProfile()); + Config::configuration()->loadDefaultProfile(); + needRebuild = true; + break; + } + } + } + + if (Config::configuration()->docRebuild() || needRebuild) { + engine->removeOldCacheFiles(); + Config::configuration()->setDocRebuild(false); + Config::configuration()->save(); + } + if (contentList.isEmpty()) + getAllContents(); + + titleMap.clear(); + for(QList<QPair<QString, ContentList> >::Iterator it = contentList.begin(); it != contentList.end(); ++it) { + ContentList lst = (*it).second; + foreach (ContentItem item, lst) { + titleMap[item.reference] = item.title.trimmed(); + } + } + done = true; + engine->mutex.lock(); + engine->titleMapDoneCondition.wakeAll(); + engine->mutex.unlock(); +} + +void TitleMapThread::getAllContents() +{ + QFile contentFile(engine->cacheFilePath() + QDir::separator() + QLatin1String("contentdb40.") + + Config::configuration()->profileName()); + contentList.clear(); + if (!contentFile.open(QFile::ReadOnly)) { + buildContentDict(); + return; + } + + QDataStream ds(&contentFile); + quint32 fileAges; + ds >> fileAges; + if (fileAges != engine->getFileAges()) { + contentFile.close(); + engine->removeOldCacheFiles(true); + buildContentDict(); + return; + } + QString key; + QList<ContentItem> lst; + while (!ds.atEnd()) { + ds >> key; + ds >> lst; + contentList += qMakePair(key, QList<ContentItem>(lst)); + } + contentFile.close(); + +} + +void TitleMapThread::buildContentDict() +{ + QStringList docuFiles = Config::configuration()->docFiles(); + + quint32 fileAges = 0; + for(QStringList::iterator it = docuFiles.begin(); it != docuFiles.end(); it++) { + QFile file(*it); + if (!file.exists()) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Documentation file %1 does not exist!\n" + "Skipping file.").arg(QFileInfo(file).absoluteFilePath())); +#endif + continue; + } + fileAges += QFileInfo(file).lastModified().toTime_t(); + DocuParser *handler = DocuParser::createParser(*it); + if(!handler) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Documentation file %1 is not compatible!\n" + "Skipping file.").arg(QFileInfo(file).absoluteFilePath())); +#endif + continue; + } + bool ok = handler->parse(&file); + file.close(); + if(ok) { + contentList += qMakePair(*it, QList<ContentItem>(handler->getContentItems())); + delete handler; + } else { +#ifdef _SHOW_ERRORS_ + QString msg = QString::fromLatin1("In file %1:\n%2") + .arg(QFileInfo(file).absoluteFilePath()) + .arg(handler->errorProtocol()); + emit errorOccured(msg); +#endif + continue; + } + } + + QFile contentOut(engine->cacheFilePath() + QDir::separator() + QLatin1String("contentdb40.") + + Config::configuration()->profileName()); + if (contentOut.open(QFile::WriteOnly)) { + QDataStream s(&contentOut); + s << fileAges; + for(QList<QPair<QString, ContentList> >::Iterator it = contentList.begin(); it != contentList.end(); ++it) { + s << *it; + } + contentOut.close(); + } +} + + + + + +IndexThread::IndexThread(HelpEngine *he) + : QThread(he) +{ + engine = he; + indexModel = new IndexListModel(this); + indexDone = false; +} + +void IndexThread::run() +{ + if (indexDone) + return; + engine->mutex.lock(); + if (engine->titleMapThread->isRunning()) + engine->titleMapDoneCondition.wait(&(engine->mutex)); + engine->mutex.unlock(); + + keywordDocuments.clear(); + QList<IndexKeyword> lst; + QFile indexFile(engine->cacheFilePath() + QDir::separator() + QLatin1String("indexdb40.") + + Config::configuration()->profileName()); + if (!indexFile.open(QFile::ReadOnly)) { + buildKeywordDB(); + if (!indexFile.open(QFile::ReadOnly)) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Failed to load keyword index file!")); +#endif + return; + } + } + + QDataStream ds(&indexFile); + quint32 fileAges; + ds >> fileAges; + if (fileAges != engine->getFileAges()) { + indexFile.close(); + buildKeywordDB(); + if (!indexFile.open(QFile::ReadOnly)) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Cannot open the index file %1") + .arg(QFileInfo(indexFile).absoluteFilePath())); +#endif + return; + } + ds.setDevice(&indexFile); + ds >> fileAges; + } + ds >> lst; + indexFile.close(); + + for (int i=0; i<lst.count(); ++i) { + const IndexKeyword &idx = lst.at(i); + indexModel->addLink(idx.keyword, idx.link); + keywordDocuments << HelpEngine::removeAnchorFromLink(idx.link); + } + indexModel->publish(); + indexDone = true; +} + +void IndexThread::buildKeywordDB() +{ + QStringList addDocuFiles = Config::configuration()->docFiles(); + QStringList::iterator i = addDocuFiles.begin(); + + int steps = 0; + for(; i != addDocuFiles.end(); i++) + steps += QFileInfo(*i).size(); + + QList<IndexKeyword> lst; + quint32 fileAges = 0; + for(i = addDocuFiles.begin(); i != addDocuFiles.end(); i++){ + QFile file(*i); + if (!file.exists()) { +#ifdef _SHOW_ERRORS_ + emit errorOccured(tr("Documentation file %1 does not exist!\n" + "Skipping file.").arg(QFileInfo(file).absoluteFilePath())); +#endif + continue; + } + fileAges += QFileInfo(file).lastModified().toTime_t(); + DocuParser *handler = DocuParser::createParser(*i); + bool ok = handler->parse(&file); + file.close(); + if(!ok){ +#ifdef _SHOW_ERRORS_ + QString msg = QString::fromLatin1("In file %1:\n%2") + .arg(QFileInfo(file).absoluteFilePath()) + .arg(handler->errorProtocol()); + emit errorOccured(msg); +#endif + delete handler; + continue; + } + + QList<IndexItem*> indLst = handler->getIndexItems(); + foreach (IndexItem *indItem, indLst) { + QFileInfo fi(indItem->reference); + lst.append(IndexKeyword(indItem->keyword, indItem->reference)); + } + delete handler; + } + if (!lst.isEmpty()) + qSort(lst); + + QFile indexout(engine->cacheFilePath() + QDir::separator() + QLatin1String("indexdb40.") + + Config::configuration()->profileName()); + if (verifyDirectory(engine->cacheFilePath()) && indexout.open(QFile::WriteOnly)) { + QDataStream s(&indexout); + s << fileAges; + s << lst; + indexout.close(); + } +} diff --git a/src/plugins/help/helpengine.h b/src/plugins/help/helpengine.h new file mode 100644 index 0000000000..9aa79f4e52 --- /dev/null +++ b/src/plugins/help/helpengine.h @@ -0,0 +1,182 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPENGINE_H +#define HELPENGINE_H + +#include <QtCore/QThread> +#include <QtCore/QPair> +#include <QtCore/QMap> +#include <QtCore/QWaitCondition> +#include <QtCore/QMutex> +#include <QtGui/QStringListModel> + +#include "docuparser.h" + +namespace Help { +namespace Internal { + +class HelpEngine; + +typedef QList<ContentItem> ContentList; + +class IndexListModel: public QStringListModel +{ +public: + IndexListModel(QObject *parent = 0) + : QStringListModel(parent) {} + + void clear() { contents.clear(); setStringList(QStringList()); } + + QString description(int index) const { return stringList().at(index); } + QStringList links(int index) const { return contents.values(stringList().at(index)); } + void addLink(const QString &description, const QString &link) { contents.insert(description, link); } + + void publish() { filter(QString(), QString()); } + + QModelIndex filter(const QString &s, const QString &real); + + virtual Qt::ItemFlags flags(const QModelIndex &index) const + { return QStringListModel::flags(index) & ~Qt::ItemIsEditable; } + +private: + QMultiMap<QString, QString> contents; +}; + +class TitleMapThread : public QThread +{ + Q_OBJECT + +public: + TitleMapThread(HelpEngine *he); + ~TitleMapThread(); + void setup(); + QList<QPair<QString, ContentList> > contents() const { return contentList; } + QMap<QString, QString> documentTitleMap() const { return titleMap; } + +signals: + void errorOccured(const QString &errMsg); + +protected: + void run(); + +private: + void getAllContents(); + void buildContentDict(); + + QList<QPair<QString, ContentList> > contentList; + QMap<QString, QString> titleMap; + + HelpEngine *engine; + bool done; +}; + +class IndexThread : public QThread +{ + Q_OBJECT + +public: + IndexThread(HelpEngine *he); + void setup(); + IndexListModel *model() const { return indexModel; } + +protected: + void run(); + +signals: + void errorOccured(const QString &errMsg); + +private: + void buildKeywordDB(); + + HelpEngine *engine; + QStringList keywordDocuments; + IndexListModel *indexModel; + bool indexDone; +}; + +class HelpEngine : public QObject +{ + Q_OBJECT + +public: + HelpEngine(QObject *parent, const QString& defaultQtVersionPath); + ~HelpEngine(); + void init(); + QList<QPair<QString, ContentList> > contents() const { return contentList; } + IndexListModel *indices(); + + QString titleOfLink(const QString &link); + static QString removeAnchorFromLink(const QString &link); + + QString home() const; + +signals: + void indexInitialized(); + void contentsInitialized(); + void errorOccured(const QString &errMsg); + +public slots: + void buildContents(); + void buildIndex(); + +private slots: + void titleMapFinished(); + void indexFinished(); + +private: + friend class TitleMapThread; + friend class IndexThread; + + void removeOldCacheFiles(bool onlyFulltextSearchIndex = false); + QString cacheFilePath() const; + quint32 getFileAges(); + + QList<QPair<QString, ContentList> > contentList; + QMap<QString, QString> titleMap; + + QString cacheFilesPath; + IndexListModel *indexModel; + + QWaitCondition titleMapDoneCondition; + QMutex mutex; + + TitleMapThread *titleMapThread; + IndexThread *indexThread; + + bool contentsOnly; +}; + +} //namespace Internal +} //namespace Help + +#endif diff --git a/src/plugins/help/helpfindsupport.cpp b/src/plugins/help/helpfindsupport.cpp new file mode 100644 index 0000000000..306a1d5f40 --- /dev/null +++ b/src/plugins/help/helpfindsupport.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "helpfindsupport.h" +#include "helpviewer.h" + +using namespace Help::Internal; + +HelpFindSupport::HelpFindSupport(CentralWidget *centralWidget) + : m_centralWidget(centralWidget) +{ +} + +HelpFindSupport::~HelpFindSupport() +{ +} + +bool HelpFindSupport::isEnabled() const +{ + return true; +} +QString HelpFindSupport::currentFindString() const +{ + Q_ASSERT(m_centralWidget); + HelpViewer* viewer = m_centralWidget->currentHelpViewer(); + if (!viewer) + return QString(); +#if defined(USE_WEBKIT) + return viewer->selectedText(); +#else + return viewer->textCursor().selectedText(); +#endif +} + + +QString HelpFindSupport::completedFindString() const { return QString(); } + +bool HelpFindSupport::findIncremental(const QString &txt, QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_centralWidget); + findFlags &= ~QTextDocument::FindBackward; + return m_centralWidget->find(txt, findFlags, true); +} + +bool HelpFindSupport::findStep(const QString &txt, QTextDocument::FindFlags findFlags) +{ + Q_ASSERT(m_centralWidget); + return m_centralWidget->find(txt, findFlags, false); +} diff --git a/src/plugins/help/helpfindsupport.h b/src/plugins/help/helpfindsupport.h new file mode 100644 index 0000000000..082e908c75 --- /dev/null +++ b/src/plugins/help/helpfindsupport.h @@ -0,0 +1,74 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPFINDSUPPORT_H +#define HELPFINDSUPPORT_H + +#include "centralwidget.h" + +#include <find/ifindsupport.h> + +namespace Help { +namespace Internal { + +class HelpFindSupport : public Find::IFindSupport +{ + Q_OBJECT + +public: + HelpFindSupport(CentralWidget *centralWidget); + ~HelpFindSupport(); + + bool isEnabled() const; + bool supportsReplace() const { return false; } + void resetIncrementalSearch() {} + void clearResults() {} + QString currentFindString() const; + QString completedFindString() const; + + bool findIncremental(const QString &txt, QTextDocument::FindFlags findFlags); + bool findStep(const QString &txt, QTextDocument::FindFlags findFlags); + bool replaceStep(const QString &, const QString &, + QTextDocument::FindFlags ) { return false; } + int replaceAll(const QString &, const QString &, + QTextDocument::FindFlags ) { return 0; } + +private: + bool find(const QString &ttf, QTextDocument::FindFlags findFlags, bool incremental); + + CentralWidget *m_centralWidget; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPFINDSUPPORT_H diff --git a/src/plugins/help/helpindexfilter.cpp b/src/plugins/help/helpindexfilter.cpp new file mode 100644 index 0000000000..b8aa2edfba --- /dev/null +++ b/src/plugins/help/helpindexfilter.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "helpindexfilter.h" +#include "helpplugin.h" + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/modemanager.h> + +#include <QtHelp/QHelpEngine> +#include <QtHelp/QHelpIndexModel> + +using namespace QuickOpen; +using namespace Help; +using namespace Help::Internal; + +Q_DECLARE_METATYPE(IQuickOpenFilter*); + +HelpIndexFilter::HelpIndexFilter(HelpPlugin *plugin, QHelpEngine *helpEngine): + m_plugin(plugin), + m_helpEngine(helpEngine), + m_icon(QIcon()) // TODO: Put an icon next to the results +{ + setIncludedByDefault(false); + setShortcutString("?"); + + connect(m_helpEngine->indexModel(), SIGNAL(indexCreated()), + this, SLOT(updateIndices())); +} + +void HelpIndexFilter::updateIndices() +{ + const QString currentFilter = m_plugin->indexFilter(); + if (!currentFilter.isEmpty()) + m_plugin->setIndexFilter(QString()); + + m_helpIndex = m_helpEngine->indexModel()->stringList(); + + if (!currentFilter.isEmpty()) + m_plugin->setIndexFilter(currentFilter); +} + +QString HelpIndexFilter::trName() const +{ + return tr("Help index"); +} + +QString HelpIndexFilter::name() const +{ + return QLatin1String("HelpIndexFilter"); +} + +IQuickOpenFilter::Priority HelpIndexFilter::priority() const +{ + return Medium; +} + +QList<FilterEntry> HelpIndexFilter::matchesFor(const QString &entry) +{ + QList<FilterEntry> entries; + foreach (const QString &string, m_helpIndex) { + if (string.contains(entry, Qt::CaseInsensitive)) { + FilterEntry entry(this, string, QVariant(), m_icon); + entries.append(entry); + } + } + return entries; +} + +void HelpIndexFilter::accept(FilterEntry selection) const +{ + QMap<QString, QUrl> links = m_helpEngine->indexModel()->linksForKeyword(selection.displayName); + if (links.size() == 1) { + emit linkActivated(links.begin().value()); + } else if (!links.isEmpty()) { + emit linksActivated(links, selection.displayName); + } +} + +void HelpIndexFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); + // Nothing to refresh +} diff --git a/src/plugins/help/helpindexfilter.h b/src/plugins/help/helpindexfilter.h new file mode 100644 index 0000000000..0d62a9b5e1 --- /dev/null +++ b/src/plugins/help/helpindexfilter.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPINDEXFILTER_H +#define HELPINDEXFILTER_H + +#include <quickopen/iquickopenfilter.h> + +#include <QtGui/QIcon> + +QT_BEGIN_NAMESPACE +class QHelpEngine; +class QUrl; +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class HelpPlugin; + +class HelpIndexFilter : public QuickOpen::IQuickOpenFilter +{ + Q_OBJECT + +public: + HelpIndexFilter(HelpPlugin *plugin, QHelpEngine *helpEngine); + + // IQuickOpenFilter + QString trName() const; + QString name() const; + Priority priority() const; + QList<QuickOpen::FilterEntry> matchesFor(const QString &entry); + void accept(QuickOpen::FilterEntry selection) const; + void refresh(QFutureInterface<void> &future); + +signals: + void linkActivated(const QUrl &link) const; + void linksActivated(const QMap<QString, QUrl> &urls, const QString &keyword) const; + +private slots: + void updateIndices(); + +private: + HelpPlugin *m_plugin; + QHelpEngine *m_helpEngine; + QStringList m_helpIndex; + QIcon m_icon; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPINDEXFILTER_H diff --git a/src/plugins/help/helpmode.cpp b/src/plugins/help/helpmode.cpp new file mode 100644 index 0000000000..b57da2da22 --- /dev/null +++ b/src/plugins/help/helpmode.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "helpmode.h" +#include "helpplugin.h" + +#include <QtCore/QLatin1String> +#include <QtGui/QWidget> +#include <coreplugin/findplaceholder.h> + +using namespace Help; +using namespace Help::Internal; + +HelpMode::HelpMode(QWidget *widget, QWidget *centralWidget, QObject *parent): + BaseMode(tr("Help"), + Constants::ID_MODE_HELP, + QIcon((QLatin1String(":/fancyactionbar/images/mode_Reference.png"))), Constants::P_MODE_HELP, widget, parent), + m_centralWidget(centralWidget) +{ + m_centralWidget->layout()->setSpacing(0); + m_centralWidget->layout()->addWidget(new Core::FindToolBarPlaceHolder(this)); +} + + diff --git a/src/plugins/help/helpmode.h b/src/plugins/help/helpmode.h new file mode 100644 index 0000000000..19b5c73a29 --- /dev/null +++ b/src/plugins/help/helpmode.h @@ -0,0 +1,61 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef HELPMODE_H +#define HELPMODE_H + +#include <QtCore/QObject> + +#include <coreplugin/basemode.h> + +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class HelpMode : public Core::BaseMode +{ + Q_OBJECT + +public: + HelpMode(QWidget *widget, QWidget *centralWidget, QObject *parent = 0); + +private: + QWidget *m_centralWidget; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPMODE_H diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp new file mode 100644 index 0000000000..17712cd576 --- /dev/null +++ b/src/plugins/help/helpplugin.cpp @@ -0,0 +1,684 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "helpplugin.h" +#include "docsettingspage.h" +#include "filtersettingspage.h" +#include "helpindexfilter.h" +#include "helpmode.h" +#include "helpviewer.h" +#include "contentwindow.h" +#include "indexwindow.h" +#include "bookmarkmanager.h" +#include "centralwidget.h" +#include "helpfindsupport.h" +#include "searchwidget.h" + +#include <QtCore/QDebug> +#include <QtCore/qplugin.h> +#include <QtCore/QFileInfo> +#include <QtCore/QSettings> +#include <QtCore/QDir> +#include <QtCore/QResource> +#include <QtGui/QAction> +#include <QtGui/QShortcut> +#include <QtGui/QSplitter> +#include <QtGui/QStyle> +#include <QtGui/QToolBar> +#include <QtGui/QComboBox> +#include <QtHelp/QHelpEngine> + +#include <extensionsystem/pluginmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/minisplitter.h> +#include <coreplugin/modemanager.h> +#include <coreplugin/rightpane.h> +#include <coreplugin/sidebar.h> +#include <coreplugin/welcomemode.h> + +using namespace Help; +using namespace Help::Internal; + +HelpManager::HelpManager(QHelpEngine *helpEngine) + : m_helpEngine(helpEngine) +{ +} + +void HelpManager::registerDocumentation(const QStringList &fileNames) +{ + bool needsSetup = false; + { + QHelpEngineCore hc(m_helpEngine->collectionFile()); + hc.setupData(); + foreach (const QString &fileName, fileNames) { + if (!QFile::exists(fileName)) + continue; + QString fileNamespace = QHelpEngineCore::namespaceName(fileName); + if (!fileNamespace.isEmpty() && !hc.registeredDocumentations().contains(fileNamespace)) { + if (hc.registerDocumentation(fileName)) + needsSetup = true; + else + qDebug() << "error registering" << fileName << hc.error(); + } + } + } + if (needsSetup) + qDebug() << m_helpEngine->setupData(); +} + +HelpPlugin::HelpPlugin() : + m_core(0), + m_helpEngine(0), + m_contextHelpEngine(0), + m_contentWidget(0), + m_indexWidget(0), + m_centralWidget(0), + m_helpViewerForSideBar(0), + m_mode(0), + m_shownLastPages(false), + m_contentItem(0), + m_indexItem(0), + m_searchItem(0), + m_bookmarkItem(0), + m_rightPaneSideBar(0) +{ +} + +HelpPlugin::~HelpPlugin() +{ +} + +bool HelpPlugin::initialize(const QStringList & /*arguments*/, QString *) +{ + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + QList<int> globalcontext; + globalcontext << Core::Constants::C_GLOBAL_ID; + QList<int> modecontext; + modecontext << m_core->uniqueIDManager()->uniqueIdentifier(Constants::C_MODE_HELP); + + // FIXME shouldn't the help engine create the directory if it doesn't exist? + QFileInfo fi(m_core->settings()->fileName()); + QDir directory(fi.absolutePath()); + if (!directory.exists()) + directory.mkpath(directory.absolutePath()); + m_helpEngine = new QHelpEngine(directory.absolutePath() + + QLatin1String("/helpcollection.qhc"), this); + connect(m_helpEngine, SIGNAL(setupFinished()), + this, SLOT(updateFilterComboBox())); + + addAutoReleasedObject(new HelpManager(m_helpEngine)); + + m_docSettingsPage = new DocSettingsPage(m_helpEngine); + addAutoReleasedObject(m_docSettingsPage); + + m_filterSettingsPage = new FilterSettingsPage(m_helpEngine); + addAutoReleasedObject(m_filterSettingsPage); + connect(m_docSettingsPage, SIGNAL(documentationAdded()), + m_filterSettingsPage, SLOT(updateFilterPage())); + connect(m_docSettingsPage, SIGNAL(dialogAccepted()), + this, SLOT(checkForHelpChanges())); + + m_contentWidget = new ContentWindow(m_helpEngine); + m_contentWidget->setWindowTitle(tr("Contents")); + m_indexWidget = new IndexWindow(m_helpEngine); + m_indexWidget->setWindowTitle(tr("Index")); + m_searchWidget = new SearchWidget(m_helpEngine->searchEngine()); + m_searchWidget->setWindowTitle(tr("Search")); + m_bookmarkManager = new BookmarkManager(m_helpEngine); + m_bookmarkWidget = new BookmarkWidget(m_bookmarkManager, 0, false); + m_bookmarkWidget->setWindowTitle(tr("Bookmarks")); + connect(m_bookmarkWidget, SIGNAL(addBookmark()), + this, SLOT(addBookmark())); + + Core::ActionManagerInterface *am = m_core->actionManager(); + Core::ICommand *cmd; + + // Add Home, Previous and Next actions (used in the toolbar) + QAction *homeAction = new QAction(QIcon(QLatin1String(":/help/images/home.png")), tr("Home"), this); + cmd = am->registerAction(homeAction, QLatin1String("Help.Home"), globalcontext); + + QAction *previousAction = new QAction(QIcon(QLatin1String(":/help/images/previous.png")), + tr("Previous"), this); + cmd = am->registerAction(previousAction, QLatin1String("Help.Previous"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_Backspace)); + + QAction *nextAction = new QAction(QIcon(QLatin1String(":/help/images/next.png")), tr("Next"), this); + cmd = am->registerAction(nextAction, QLatin1String("Help.Next"), modecontext); + + QAction *addBookmarkAction = new QAction(QIcon(QLatin1String(":/help/images/bookmark.png")), + tr("Add Bookmark"), this); + cmd = am->registerAction(addBookmarkAction, QLatin1String("Help.AddBookmark"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_M)); + + // Add Index, Contents, and Context menu items and a separator to the Help menu + QAction *indexAction = new QAction(tr("Index"), this); + cmd = am->registerAction(indexAction, QLatin1String("Help.Index"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + + QAction *contentsAction = new QAction(tr("Contents"), this); + cmd = am->registerAction(contentsAction, QLatin1String("Help.Contents"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + + QAction *searchAction = new QAction(tr("Search"), this); + cmd = am->registerAction(searchAction, QLatin1String("Help.Search"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + + QAction *contextAction = new QAction(tr("Context Help"), this); + cmd = am->registerAction(contextAction, QLatin1String("Help.Context"), globalcontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F1)); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); + +#ifndef Q_OS_MAC + QAction *sep = new QAction(this); + sep->setSeparator(true); + cmd = am->registerAction(sep, QLatin1String("Help.Separator"), globalcontext); + am->actionContainer(Core::Constants::M_HELP)->addAction(cmd, Core::Constants::G_HELP_HELP); +#endif + + m_centralWidget = new CentralWidget(m_helpEngine); + Aggregation::Aggregate *agg = new Aggregation::Aggregate; + agg->add(m_centralWidget); + agg->add(new HelpFindSupport(m_centralWidget)); + QWidget *mainWidget = new QWidget; + QVBoxLayout *mainWidgetLayout = new QVBoxLayout(mainWidget); + mainWidgetLayout->setMargin(0); + mainWidgetLayout->setSpacing(0); + mainWidgetLayout->addWidget(createToolBar()); + mainWidgetLayout->addWidget(m_centralWidget); + + m_contentItem = new Core::SideBarItem(m_contentWidget); + m_indexItem = new Core::SideBarItem(m_indexWidget); + m_searchItem = new Core::SideBarItem(m_searchWidget); + m_bookmarkItem = new Core::SideBarItem(m_bookmarkWidget); + QList<Core::SideBarItem*> itemList; + itemList << m_contentItem << m_indexItem << m_searchItem << m_bookmarkItem; + m_sideBar = new Core::SideBar(itemList, QList<Core::SideBarItem*>() << m_indexItem); + + QSplitter *splitter = new Core::MiniSplitter; + splitter->setOpaqueResize(false); + splitter->addWidget(m_sideBar); + splitter->addWidget(mainWidget); + splitter->setStretchFactor(0, 0); + splitter->setStretchFactor(1, 1); + splitter->setSizes(QList<int>() << 300 << 300); + + m_mode = new HelpMode(splitter, m_centralWidget); + m_mode->setContext(QList<int>() << modecontext); + addAutoReleasedObject(m_mode); + + QAction *printAction = new QAction(this); + am->registerAction(printAction, Core::Constants::PRINT, modecontext); + connect(printAction, SIGNAL(triggered()), m_centralWidget, SLOT(print())); + + QAction *copyAction = new QAction(this); + cmd = am->registerAction(copyAction, Core::Constants::COPY, modecontext); + connect(copyAction, SIGNAL(triggered()), m_centralWidget, SLOT(copy())); + copyAction->setText(cmd->action()->text()); + copyAction->setIcon(cmd->action()->icon()); + + QMap<QString, Core::ICommand*> shortcutMap; + QShortcut *shortcut = new QShortcut(splitter); + shortcut->setWhatsThis(tr("Activate Index in Help mode")); + cmd = am->registerShortcut(shortcut, QLatin1String("Help.IndexShortcut"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_I)); + connect(shortcut, SIGNAL(activated()), this, SLOT(activateIndex())); + shortcutMap.insert(m_indexWidget->windowTitle(), cmd); + + shortcut = new QShortcut(splitter); + shortcut->setWhatsThis(tr("Activate Contents in Help mode")); + cmd = am->registerShortcut(shortcut, QLatin1String("Help.ContentsShortcut"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_T)); + connect(shortcut, SIGNAL(activated()), this, SLOT(activateContents())); + shortcutMap.insert(m_contentWidget->windowTitle(), cmd); + + shortcut = new QShortcut(splitter); + shortcut->setWhatsThis(tr("Activate Search in Help mode")); + cmd = am->registerShortcut(shortcut, QLatin1String("Help.SearchShortcut"), modecontext); + cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::Key_S)); + connect(shortcut, SIGNAL(activated()), this, SLOT(activateSearch())); + shortcutMap.insert(m_searchWidget->windowTitle(), cmd); + shortcutMap.insert(m_bookmarkWidget->windowTitle(), 0); + + m_sideBar->setShortcutMap(shortcutMap); + + connect(homeAction, SIGNAL(triggered()), m_centralWidget, SLOT(home())); + connect(previousAction, SIGNAL(triggered()), m_centralWidget, SLOT(backward())); + connect(nextAction, SIGNAL(triggered()), m_centralWidget, SLOT(forward())); + connect(addBookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark())); + connect(m_contentWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_indexWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_searchWidget, SIGNAL(requestShowLink(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_searchWidget, SIGNAL(requestShowLinkInNewTab(const QUrl&)), + m_centralWidget, SLOT(setSourceInNewTab(const QUrl&))); + connect(m_bookmarkWidget, SIGNAL(requestShowLink(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + + connect(m_centralWidget, SIGNAL(backwardAvailable(bool)), + previousAction, SLOT(setEnabled(bool))); + connect(m_centralWidget, SIGNAL(forwardAvailable(bool)), + nextAction, SLOT(setEnabled(bool))); + connect(m_centralWidget, SIGNAL(addNewBookmark(const QString&, const QString&)), + this, SLOT(addNewBookmark(const QString&, const QString&))); + + QList<QAction*> actionList; + actionList << previousAction + << nextAction + << homeAction +#ifndef Q_OS_MAC + << sep +#endif + << copyAction; + m_centralWidget->setGlobalActions(actionList); + + connect(contextAction, SIGNAL(triggered()), this, SLOT(activateContext())); + connect(indexAction, SIGNAL(triggered()), this, SLOT(activateIndex())); + connect(contentsAction, SIGNAL(triggered()), this, SLOT(activateContents())); + connect(searchAction, SIGNAL(triggered()), this, SLOT(activateSearch())); + + connect(m_core->modeManager(), SIGNAL(currentModeChanged(Core::IMode*)), + this, SLOT(modeChanged(Core::IMode*))); + + connect(m_contentWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_indexWidget, SIGNAL(linkActivated(const QUrl&)), + m_centralWidget, SLOT(setSource(const QUrl&))); + connect(m_indexWidget, SIGNAL(linksActivated(const QMap<QString, QUrl>&, const QString&)), + m_centralWidget, SLOT(showTopicChooser(const QMap<QString, QUrl>&, const QString&))); + + HelpIndexFilter *helpIndexFilter = new HelpIndexFilter(this, m_helpEngine); + addAutoReleasedObject(helpIndexFilter); + connect(helpIndexFilter, SIGNAL(linkActivated(QUrl)), + this, SLOT(switchToHelpMode(QUrl))); + connect(helpIndexFilter, SIGNAL(linksActivated(const QMap<QString, QUrl>&, const QString&)), + this, SLOT(switchToHelpMode(const QMap<QString, QUrl>&, const QString&))); + + previousAction->setEnabled(m_centralWidget->isBackwardAvailable()); + nextAction->setEnabled(m_centralWidget->isForwardAvailable()); + + createRightPaneSideBar(); + + return true; +} + +void HelpPlugin::createRightPaneSideBar() +{ + QAction *switchToHelpMode = new QAction("Go to Help Mode", this); + m_rightPaneBackwardAction = new QAction(QIcon(QLatin1String(":/help/images/previous.png")), tr("Previous"), this); + m_rightPaneForwardAction = new QAction(QIcon(QLatin1String(":/help/images/next.png")), tr("Next"), this); + + QToolBar *rightPaneToolBar = new QToolBar(); + rightPaneToolBar->addAction(switchToHelpMode); + rightPaneToolBar->addAction(m_rightPaneBackwardAction); + rightPaneToolBar->addAction(m_rightPaneForwardAction); + + connect(switchToHelpMode, SIGNAL(triggered()), this, SLOT(switchToHelpMode())); + connect(m_rightPaneBackwardAction, SIGNAL(triggered()), this, SLOT(rightPaneBackward())); + connect(m_rightPaneForwardAction, SIGNAL(triggered()), this, SLOT(rightPaneForward())); + + QToolButton *closeButton = new QToolButton(); + closeButton->setProperty("type", QLatin1String("dockbutton")); + closeButton->setIcon(QIcon(":/qworkbench/images/closebutton.png")); + + // Dummy layout to align the close button to the right + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout->setSpacing(0); + hboxLayout->setMargin(0); + hboxLayout->addStretch(5); + hboxLayout->addWidget(closeButton); + + QWidget *w = new QWidget(rightPaneToolBar); + w->setLayout(hboxLayout); + rightPaneToolBar->addWidget(w); + connect(closeButton, SIGNAL(clicked()), this, SLOT(slotHideRightPane())); + + QVBoxLayout *rightPaneLayout = new QVBoxLayout; + rightPaneLayout->setMargin(0); + rightPaneLayout->setSpacing(0); + rightPaneLayout->addWidget(rightPaneToolBar); + + m_helpViewerForSideBar = new HelpViewer(m_helpEngine, 0); + rightPaneLayout->addWidget(m_helpViewerForSideBar); + + m_rightPaneSideBar = new QWidget; + m_rightPaneSideBar->setLayout(rightPaneLayout); + m_rightPaneSideBar->setFocusProxy(m_helpViewerForSideBar); + addAutoReleasedObject(new Core::BaseRightPaneWidget(m_rightPaneSideBar)); +} + +void HelpPlugin::rightPaneBackward() +{ + m_helpViewerForSideBar->backward(); +} + +void HelpPlugin::rightPaneForward() +{ + m_helpViewerForSideBar->forward(); +} + +void HelpPlugin::switchToHelpMode() +{ + switchToHelpMode(m_helpViewerForSideBar->source()); + Core::RightPaneWidget::instance()->setShown(false); +} + +void HelpPlugin::switchToHelpMode(const QUrl &source) +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_centralWidget->setSource(source); + m_centralWidget->setFocus(); +} + +void HelpPlugin::switchToHelpMode(const QMap<QString, QUrl> &urls, const QString &keyword) +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_centralWidget->showTopicChooser(urls, keyword); +} + +void HelpPlugin::slotHideRightPane() +{ + Core::RightPaneWidget::instance()->setShown(false); +} + +void HelpPlugin::extensionsInitialized() +{ + m_sideBar->readSettings(m_core->settings()); + if (!m_helpEngine->setupData()) { + qWarning() << "Could not initialize help engine: " << m_helpEngine->error(); + return; + } + + bool needsSetup = false; + bool assistantInternalDocRegistered = false; + + foreach (QString ns, m_helpEngine->registeredDocumentations()) { + if (ns == QString("com.nokia.qtcreator.%1%2") + .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR)) { + assistantInternalDocRegistered = true; + break; + } + } + + if (!assistantInternalDocRegistered) { + QFileInfo fi(m_helpEngine->collectionFile()); + + const QString qchFileName = + QDir::cleanPath(QCoreApplication::applicationDirPath() +#if defined(Q_OS_MAC) + + QLatin1String("/../Resources/doc/qtcreator.qch")); +#else + + QLatin1String("/../doc/qtcreator.qch")); +#endif + QHelpEngineCore hc(fi.absoluteFilePath()); + hc.setupData(); + if (!hc.registerDocumentation(qchFileName)) + qDebug() << hc.error(); + needsSetup = true; + } + + int i = m_helpEngine->customValue( + QLatin1String("UnfilteredFilterInserted")).toInt(); + if (i != 1) { + { + QHelpEngineCore hc(m_helpEngine->collectionFile()); + hc.setupData(); + hc.addCustomFilter(tr("Unfiltered"), QStringList()); + hc.setCustomValue(QLatin1String("UnfilteredFilterInserted"), 1); + } + m_helpEngine->blockSignals(true); + m_helpEngine->setCurrentFilter(tr("Unfiltered")); + m_helpEngine->blockSignals(false); + needsSetup = true; + } + + if (needsSetup) + m_helpEngine->setupData(); + + updateFilterComboBox(); + m_bookmarkManager->setupBookmarkModels(); + + if (Core::Internal::WelcomeMode *welcomeMode = qobject_cast<Core::Internal::WelcomeMode*>(m_core->modeManager()->mode(Core::Constants::MODE_WELCOME))) { + connect(welcomeMode, SIGNAL(requestHelp(QString)), this, SLOT(openGettingStarted())); + } +} + +void HelpPlugin::shutdown() +{ + m_sideBar->saveSettings(m_core->settings()); + m_bookmarkManager->saveBookmarks(); + delete m_bookmarkManager; +} + +void HelpPlugin::setIndexFilter(const QString &filter) +{ + m_indexWidget->setSearchLineEditText(filter); +} + +QString HelpPlugin::indexFilter() const +{ + return m_indexWidget->searchLineEditText(); +} + +void HelpPlugin::modeChanged(Core::IMode *mode) +{ + if (mode == m_mode && !m_shownLastPages) { + m_shownLastPages = true; + qApp->processEvents(); + qApp->setOverrideCursor(Qt::WaitCursor); + m_centralWidget->setLastShownPages(); + qApp->restoreOverrideCursor(); + } +} + +void HelpPlugin::activateContext() +{ + using namespace Core; + // case 1 sidebar shown and has focus, we show whatever we have in the + // sidebar in big + RightPanePlaceHolder* placeHolder = RightPanePlaceHolder::current(); + if(placeHolder && Core::RightPaneWidget::instance()->hasFocus()) { + switchToHelpMode(); + return; + } + + bool useSideBar = false; + if (placeHolder && !Core::RightPaneWidget::instance()->hasFocus()) + useSideBar = true; + + // Find out what to show + HelpViewer *viewer = 0; + if (IContext *context = m_core->currentContextObject()) { + if (!m_contextHelpEngine) { + m_contextHelpEngine = new QHelpEngineCore(m_helpEngine->collectionFile(), this); + //m_contextHelpEngine->setAutoSaveFilter(false); + m_contextHelpEngine->setupData(); + m_contextHelpEngine->setCurrentFilter(tr("Unfiltered")); + } + + const QString &id = context->contextHelpId(); + QMap<QString, QUrl> links = m_contextHelpEngine->linksForIdentifier(id); + if (!links.isEmpty()) { + if (useSideBar) { + Core::RightPaneWidget::instance()->setShown(true); + viewer = m_helpViewerForSideBar; + } else { + viewer = m_centralWidget->currentHelpViewer(); + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + } + + if (viewer) { + QUrl source = *links.begin(); + if (viewer->source() != source) + viewer->setSource(source); + viewer->setFocus(); + } + } else { + // No link found + if (useSideBar) { + Core::RightPaneWidget::instance()->setShown(true); + viewer = m_helpViewerForSideBar; + } else { + viewer = m_centralWidget->currentHelpViewer(); + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + } + + if (viewer) { + viewer->setHtml(tr("<title>No Documentation</title><br><br>" + "<center><b>%1</b><br><br>No documentation available."). + arg(id)); + viewer->setSource(QUrl()); + //activateIndex(); + } + } + } else { + // No context object + if (useSideBar) { + Core::RightPaneWidget::instance()->setShown(true); + viewer = m_helpViewerForSideBar; + } else { + viewer = m_centralWidget->currentHelpViewer(); + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + } + + if (viewer) { + viewer->setSource(QUrl()); + viewer->setHtml("<title>No Documentation</title><br><br><center>No" + " documentation available."); + //activateIndex(); + } + } +} + +void HelpPlugin::activateIndex() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_sideBar->activateItem(m_indexItem); +} + +void HelpPlugin::activateContents() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_sideBar->activateItem(m_contentItem); +} + +void HelpPlugin::activateSearch() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_sideBar->activateItem(m_searchItem); +} + +QToolBar *HelpPlugin::createToolBar() +{ + QToolBar *toolWidget = new QToolBar; + Core::ActionManagerInterface *am = m_core->actionManager(); + 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()); + //int size = toolWidget->style()->pixelMetric(QStyle::PM_SmallIconSize); + //toolWidget->setIconSize(QSize(size, size)); + toolWidget->setMovable(false); + + toolWidget->addSeparator(); + + QWidget *w = new QWidget; + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->addSpacing(10); + layout->addWidget(new QLabel(tr("Filtered by:"))); + m_filterComboBox = new QComboBox; + m_filterComboBox->setMinimumContentsLength(20); + connect(m_filterComboBox, SIGNAL(activated(const QString&)), + this, SLOT(filterDocumentation(const QString&))); + layout->addWidget(m_filterComboBox); + toolWidget->addWidget(w); + + return toolWidget; +} + +void HelpPlugin::updateFilterComboBox() +{ + QString curFilter = m_filterComboBox->currentText(); + if (curFilter.isEmpty()) + curFilter = m_helpEngine->currentFilter(); + m_filterComboBox->clear(); + m_filterComboBox->addItems(m_helpEngine->customFilters()); + int idx = m_filterComboBox->findText(curFilter); + if (idx < 0) + idx = 0; + m_filterComboBox->setCurrentIndex(idx); +} + +void HelpPlugin::checkForHelpChanges() +{ + bool changed = m_docSettingsPage->applyChanges(); + changed |= m_filterSettingsPage->applyChanges(); + if (changed) + m_helpEngine->setupData(); +} + +void HelpPlugin::filterDocumentation(const QString &customFilter) +{ + m_helpEngine->setCurrentFilter(customFilter); +} + +void HelpPlugin::addBookmark() +{ + addNewBookmark(m_centralWidget->currentTitle(), m_centralWidget->currentSource().toString()); +} + +void HelpPlugin::addNewBookmark(const QString &title, const QString &url) +{ + if (url.isEmpty()) + return; + + m_bookmarkManager->showBookmarkDialog(m_centralWidget, title, url); +} + +void HelpPlugin::openGettingStarted() +{ + m_core->modeManager()->activateMode(QLatin1String(Constants::ID_MODE_HELP)); + m_centralWidget->setSource( + QString("qthelp://com.nokia.qtcreator.%1%2/doc/index.html") + .arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR)); +} + + +Q_EXPORT_PLUGIN(HelpPlugin) diff --git a/src/plugins/help/helpplugin.h b/src/plugins/help/helpplugin.h new file mode 100644 index 0000000000..21dab96dc9 --- /dev/null +++ b/src/plugins/help/helpplugin.h @@ -0,0 +1,169 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef HELPPLUGIN_H +#define HELPPLUGIN_H + +#include "help_global.h" +#include <extensionsystem/iplugin.h> + +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE +class QAction; +class QComboBox; +class QHelpEngineCore; +class QHelpEngine; +class QShortcut; +class QToolBar; +class QUrl; + +class IndexWindow; +class ContentWindow; +class BookmarkManager; +class BookmarkWidget; +class CentralWidget; +class HelpViewer; +QT_END_NAMESPACE + + +namespace Core { +class ICore; +class IMode; +class SideBar; +class SideBarItem; +} + +namespace Help { + +namespace Constants { + const char * const HELPVIEWER_KIND = "Qt Help Viewer"; + const char * const C_MODE_HELP = "Help Mode"; + const int P_MODE_HELP = 70; + const char * const ID_MODE_HELP = "Help.HelpMode"; +} + +class HELP_EXPORT HelpManager : public QObject +{ + Q_OBJECT +public: + HelpManager(QHelpEngine *helpEngine); + + void registerDocumentation(const QStringList &fileNames); + +private: + QHelpEngine *m_helpEngine; +}; + +namespace Internal { + +class HelpMode; +class HelpPluginEditorFactory; +class DocSettingsPage; +class FilterSettingsPage; +class SearchWidget; + +class HelpPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + HelpPlugin(); + virtual ~HelpPlugin(); + + bool initialize(const QStringList &arguments, QString *error_message); + void extensionsInitialized(); + void shutdown(); + + // Necessary to get the unfiltered list in the help index filter + void setIndexFilter(const QString &filter); + QString indexFilter() const; + +private slots: + void modeChanged(Core::IMode *mode); + void activateContext(); + void activateIndex(); + void activateContents(); + void activateSearch(); + void checkForHelpChanges(); + void updateFilterComboBox(); + void filterDocumentation(const QString &customFilter); + void addBookmark(); + void addNewBookmark(const QString &title, const QString &url); + + void rightPaneBackward(); + void rightPaneForward(); + void switchToHelpMode(); + void switchToHelpMode(const QUrl &source); + void switchToHelpMode(const QMap<QString, QUrl> &urls, const QString &keyword); + void slotHideRightPane(); + + void openGettingStarted(); + +private: + QToolBar *createToolBar(); + void createRightPaneSideBar(); + + Core::ICore *m_core; + QHelpEngine *m_helpEngine; + QHelpEngineCore *m_contextHelpEngine; + ContentWindow *m_contentWidget; + IndexWindow *m_indexWidget; + BookmarkWidget *m_bookmarkWidget; + BookmarkManager *m_bookmarkManager; + SearchWidget *m_searchWidget; + CentralWidget *m_centralWidget; + HelpViewer *m_helpViewerForSideBar; + HelpMode *m_mode; + bool m_shownLastPages; + + Core::SideBarItem *m_contentItem; + Core::SideBarItem *m_indexItem; + Core::SideBarItem *m_searchItem; + Core::SideBarItem *m_bookmarkItem; + + DocSettingsPage *m_docSettingsPage; + FilterSettingsPage *m_filterSettingsPage; + + QComboBox *m_filterComboBox; + Core::SideBar *m_sideBar; + QWidget *m_rightPaneSideBar; + + QAction *m_rightPaneBackwardAction; + QAction *m_rightPaneForwardAction; +}; + +} // namespace Internal +} // namespace Help + +#endif // HELPPLUGIN_H diff --git a/src/plugins/help/images/book.png b/src/plugins/help/images/book.png Binary files differnew file mode 100644 index 0000000000..ecd311b31f --- /dev/null +++ b/src/plugins/help/images/book.png diff --git a/src/plugins/help/images/bookmark.png b/src/plugins/help/images/bookmark.png Binary files differnew file mode 100644 index 0000000000..7b2e5fd0ce --- /dev/null +++ b/src/plugins/help/images/bookmark.png diff --git a/src/plugins/help/images/find.png b/src/plugins/help/images/find.png Binary files differnew file mode 100644 index 0000000000..fafcd3bb21 --- /dev/null +++ b/src/plugins/help/images/find.png diff --git a/src/plugins/help/images/home.png b/src/plugins/help/images/home.png Binary files differnew file mode 100644 index 0000000000..9cee3025d0 --- /dev/null +++ b/src/plugins/help/images/home.png diff --git a/src/plugins/help/images/mac/addtab.png b/src/plugins/help/images/mac/addtab.png Binary files differnew file mode 100644 index 0000000000..20928fb402 --- /dev/null +++ b/src/plugins/help/images/mac/addtab.png diff --git a/src/plugins/help/images/mac/closetab.png b/src/plugins/help/images/mac/closetab.png Binary files differnew file mode 100644 index 0000000000..ab9d669eee --- /dev/null +++ b/src/plugins/help/images/mac/closetab.png diff --git a/src/plugins/help/images/next.png b/src/plugins/help/images/next.png Binary files differnew file mode 100644 index 0000000000..7700d6fce6 --- /dev/null +++ b/src/plugins/help/images/next.png diff --git a/src/plugins/help/images/previous.png b/src/plugins/help/images/previous.png Binary files differnew file mode 100644 index 0000000000..99dc8733c7 --- /dev/null +++ b/src/plugins/help/images/previous.png diff --git a/src/plugins/help/images/win/addtab.png b/src/plugins/help/images/win/addtab.png Binary files differnew file mode 100644 index 0000000000..4bb0feb92d --- /dev/null +++ b/src/plugins/help/images/win/addtab.png diff --git a/src/plugins/help/images/win/closetab.png b/src/plugins/help/images/win/closetab.png Binary files differnew file mode 100644 index 0000000000..ef9e02086c --- /dev/null +++ b/src/plugins/help/images/win/closetab.png diff --git a/src/plugins/help/indextoolwindow.cpp b/src/plugins/help/indextoolwindow.cpp new file mode 100644 index 0000000000..f80bd453cb --- /dev/null +++ b/src/plugins/help/indextoolwindow.cpp @@ -0,0 +1,215 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include <QtCore/QDebug> +#include <QtGui/QKeyEvent> +#include <QtGui/QFocusEvent> +#include <QtGui/QLayout> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QListView> +#include <QtGui/QApplication> + +#include "indextoolwindow.h" +#include "helpengine.h" +#include "topicchooser.h" + +using namespace Help::Internal; + +IndexToolWidget::IndexToolWidget() +{ + wasInitialized = false; + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + + QLabel *l = new QLabel(tr("Look for:"), this); + layout->addWidget(l); + + findLineEdit = new QLineEdit(this); + findLineEdit->installEventFilter(this); + layout->addWidget(findLineEdit); + + indicesView = new QListView(this); + indicesView->setLayoutMode(QListView::Batched); + indicesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + layout->addWidget(indicesView); + + setWindowTitle(tr("Index")); + setWindowIcon(QIcon(":/help/images/find.png")); +} + +void IndexToolWidget::focusInEvent(QFocusEvent *e) +{ + showEvent(0); + if (e && e->reason() != Qt::MouseFocusReason) { + findLineEdit->selectAll(); + findLineEdit->setFocus(); + } +} + +void IndexToolWidget::showEvent(QShowEvent *) +{ + if (!wasInitialized) { + wasInitialized = true; + setCursor(QCursor(Qt::WaitCursor)); + emit buildRequested(); + } +} + +bool IndexToolWidget::eventFilter(QObject * o, QEvent * e) +{ + if (o == findLineEdit && e->type() == QEvent::KeyPress) { + switch (static_cast<QKeyEvent*>(e)->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_PageDown: + case Qt::Key_PageUp: + QApplication::sendEvent(indicesView, e); + break; + case Qt::Key_Escape: + emit escapePressed(); + break; + default: + break; + } + } + return QWidget::eventFilter(o, e); +} + + +IndexToolWindow::IndexToolWindow(const QList<int> &context, HelpEngine *help) +{ + m_context = context; + m_context << 0; + + m_widget = new IndexToolWidget; + + helpEngine = help; + connect(helpEngine, SIGNAL(indexInitialized()), this, SLOT(indexDone())); + model = 0; + + connect(m_widget->findLineEdit, SIGNAL(textEdited(const QString&)), + this, SLOT(searchInIndex(const QString &))); + connect(m_widget->findLineEdit, SIGNAL(returnPressed()), this, SLOT(indexRequested())); + connect(m_widget, SIGNAL(buildRequested()), helpEngine, SLOT(buildIndex())); + + connect(m_widget->indicesView, SIGNAL(activated(const QModelIndex&)), + this, SLOT(indexRequested())); + connect(m_widget, SIGNAL(escapePressed()), this, SIGNAL(escapePressed())); + +} + +IndexToolWindow::~IndexToolWindow() +{ + delete m_widget; +} + +const QList<int> &IndexToolWindow::context() const +{ + return m_context; +} + +QWidget *IndexToolWindow::widget() +{ + return m_widget; +} + +void IndexToolWindow::indexDone() +{ + model = helpEngine->indices(); + m_widget->indicesView->setModel(model); + m_widget->setCursor(QCursor(Qt::ArrowCursor)); +} + +void IndexToolWindow::searchInIndex(const QString &str) +{ + if (!model) + return; + QRegExp atoz("[A-Z]"); + int matches = str.count(atoz); + if (matches > 0 && !str.contains(".*")) + { + int start = 0; + QString newSearch; + for (; matches > 0; --matches) { + int match = str.indexOf(atoz, start+1); + if (match <= start) + continue; + newSearch += str.mid(start, match-start); + newSearch += ".*"; + start = match; + } + newSearch += str.mid(start); + m_widget->indicesView->setCurrentIndex(model->filter(newSearch, str)); + } + else + m_widget->indicesView->setCurrentIndex(model->filter(str, str)); +} + +void IndexToolWindow::indexRequested() +{ + if (!model) + return; + int row = m_widget->indicesView->currentIndex().row(); + if (row == -1 || row >= model->rowCount()) + return; + + QString description = model->description(row); + QStringList links = model->links(row); + + bool blocked = m_widget->findLineEdit->blockSignals(true); + m_widget->findLineEdit->setText(description); + m_widget->findLineEdit->blockSignals(blocked); + + if (links.count() == 1) { + emit showLinkRequested(links.first(), false); + } else { + qSort(links); + QStringList::Iterator it = links.begin(); + QStringList linkList; + QStringList linkNames; + for (; it != links.end(); ++it) { + linkList << *it; + linkNames << helpEngine->titleOfLink(*it); + } + QString link = TopicChooser::getLink(m_widget, linkNames, linkList, description); + if (!link.isEmpty()) + emit showLinkRequested(link, false); + } + + model->publish(); + m_widget->indicesView->setCurrentIndex(model->index(model->stringList().indexOf(description))); + m_widget->indicesView->scrollTo(m_widget->indicesView->currentIndex(), QAbstractItemView::PositionAtTop); +} + diff --git a/src/plugins/help/indextoolwindow.h b/src/plugins/help/indextoolwindow.h new file mode 100644 index 0000000000..4d4eb8f6b2 --- /dev/null +++ b/src/plugins/help/indextoolwindow.h @@ -0,0 +1,113 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef INDEXTOOLWINDOW_H +#define INDEXTOOLWINDOW_H + +#include <QtCore/QModelIndex> +#include <QtGui/QWidget> + +#include <coreplugin/iview.h> + +class QListView; +class QLineEdit; + +namespace Help { +namespace Internal { + +class HelpEngine; +class IndexListModel; +class IndexToolWindow; + +class IndexToolWidget : public QWidget +{ + Q_OBJECT +public: + IndexToolWidget(); + +signals: + void buildRequested(); + void escapePressed(); + +private: + friend class IndexToolWindow; + + bool eventFilter(QObject * o, QEvent * e); + void showEvent(QShowEvent *e); + void focusInEvent(QFocusEvent *e); + + bool wasInitialized; + QLineEdit *findLineEdit; + QListView *indicesView; +}; + + +class IndexToolWindow : public Core::IView +{ + Q_OBJECT + +public: + IndexToolWindow(const QList<int> &context, HelpEngine *help); + ~IndexToolWindow(); + + const QList<int> &context() const; + QWidget *widget(); + + QList<QWidget*> dockToolBarWidgets() const { return QList<QWidget*>(); } + + const char *uniqueViewName() const { return "Help.IndexToolWindow"; } + const char *globalMenuGroup() const { return "Help.Group"; } + inline QKeySequence defaultShortcut() const { return QKeySequence(); } + Qt::DockWidgetArea defaultArea() const { return Qt::RightDockWidgetArea; } + IView::ViewPosition defaultPosition() const { return IView::Second; } + +signals: + void showLinkRequested(const QString &link, bool newWindow); + void escapePressed(); + +private slots: + void indexDone(); + void searchInIndex(const QString &str); + void indexRequested(); + +private: + HelpEngine *helpEngine; + IndexListModel *model; + + QList<int> m_context; + IndexToolWidget *m_widget; +}; + +} // namespace Internal +} // namespace Help + +#endif // INDEXTOOLWINDOW_H diff --git a/src/plugins/help/indexwindow.h b/src/plugins/help/indexwindow.h new file mode 100644 index 0000000000..a0e43a3f2a --- /dev/null +++ b/src/plugins/help/indexwindow.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef INDEXWINDOW +#define INDEXWINDOW + +#include <QtCore/QUrl> +#include <QtGui/QWidget> +#include <QtGui/QLineEdit> + +QT_BEGIN_NAMESPACE + +class QHelpIndexWidget; +class QHelpEngine; + +class IndexWindow : public QWidget +{ + Q_OBJECT + +public: + IndexWindow(QHelpEngine *helpEngine, QWidget *parent = 0); + ~IndexWindow(); + + void setSearchLineEditText(const QString &text); + QString searchLineEditText() const + { + return m_searchLineEdit->text(); + } + +signals: + void linkActivated(const QUrl &link); + void linksActivated(const QMap<QString, QUrl> &links, + const QString &keyword); + void escapePressed(); + +private slots: + void filterIndices(const QString &filter); + void enableSearchLineEdit(); + void disableSearchLineEdit(); + +private: + bool eventFilter(QObject *obj, QEvent *e); + void focusInEvent(QFocusEvent *e); + + QLineEdit *m_searchLineEdit; + QHelpIndexWidget *m_indexWidget; + QHelpEngine *m_helpEngine; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/help/searchwidget.cpp b/src/plugins/help/searchwidget.cpp new file mode 100644 index 0000000000..620ca7ef76 --- /dev/null +++ b/src/plugins/help/searchwidget.cpp @@ -0,0 +1,213 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "searchwidget.h" + +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> + +#include <QtGui/QMenu> +#include <QtGui/QLayout> +#include <QtGui/QKeyEvent> +#include <QtGui/QClipboard> +#include <QtGui/QApplication> +#include <QtGui/QTextBrowser> + +#include <QtHelp/QHelpSearchEngine> +#include <QtHelp/QHelpSearchQueryWidget> +#include <QtHelp/QHelpSearchResultWidget> + +using namespace Help::Internal; + +SearchWidget::SearchWidget(QHelpSearchEngine *engine, QWidget *parent) + : QWidget(parent) + , zoomCount(0) + , searchEngine(engine) +{ + QVBoxLayout *vLayout = new QVBoxLayout(this); + + resultWidget = searchEngine->resultWidget(); + QHelpSearchQueryWidget *queryWidget = searchEngine->queryWidget(); + + vLayout->addWidget(queryWidget); + vLayout->addWidget(resultWidget); + + setFocusProxy(queryWidget); + + connect(queryWidget, SIGNAL(search()), this, SLOT(search())); + connect(resultWidget, SIGNAL(requestShowLink(const QUrl&)), + this, SIGNAL(requestShowLink(const QUrl&))); + + connect(searchEngine, SIGNAL(searchingStarted()), this, SLOT(searchingStarted())); + connect(searchEngine, SIGNAL(searchingFinished(int)), this, SLOT(searchingFinished(int))); +} + +SearchWidget::~SearchWidget() +{ + // nothing todo +} + +void SearchWidget::zoomIn() +{ +#ifndef QT_CLUCENE_SUPPORT + return; +#endif + + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (browser && zoomCount != 10) { + zoomCount++; + browser->zoomIn(); + } +} + +void SearchWidget::zoomOut() +{ +#ifndef QT_CLUCENE_SUPPORT + return; +#endif + + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (browser && zoomCount != -5) { + zoomCount--; + browser->zoomOut(); + } +} + +void SearchWidget::resetZoom() +{ +#ifndef QT_CLUCENE_SUPPORT + return; +#endif + + if (zoomCount == 0) + return; + + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (browser) { + browser->zoomOut(zoomCount); + zoomCount = 0; + } +} + +void SearchWidget::search() const +{ + QList<QHelpSearchQuery> query = searchEngine->queryWidget()->query(); + searchEngine->search(query); +} + +void SearchWidget::searchingStarted() +{ + qApp->setOverrideCursor(QCursor(Qt::WaitCursor)); +} + +void SearchWidget::searchingFinished(int hits) +{ + Q_UNUSED(hits) + qApp->restoreOverrideCursor(); +} + +void SearchWidget::keyPressEvent(QKeyEvent *keyEvent) +{ + if (keyEvent->key() == Qt::Key_Escape) + emit escapePressed(); +} + +void SearchWidget::contextMenuEvent(QContextMenuEvent *contextMenuEvent) +{ + QMenu menu; + QPoint point = contextMenuEvent->globalPos(); + +#ifdef QT_CLUCENE_SUPPORT + QTextBrowser* browser = qFindChild<QTextBrowser*>(resultWidget); + if (!browser) + return; + + point = browser->mapFromGlobal(point); + if (!browser->rect().contains(point, true)) + return; + + QUrl link = browser->anchorAt(point); + + QAction *copyAction = menu.addAction(tr("&Copy") + + QString(QLatin1String("\t") + QString(QKeySequence(Qt::CTRL | Qt::Key_C)))); + copyAction->setEnabled(QTextCursor(browser->textCursor()).hasSelection()); + + QAction *copyAnchorAction = menu.addAction(tr("Copy &Link Location")); + copyAnchorAction->setEnabled(!link.isEmpty() && link.isValid()); + + QAction *newTabAction = menu.addAction(tr("Open Link in New Tab") + + QString(QLatin1String("\t") + QString(QKeySequence(Qt::CTRL))) + + QLatin1String("LMB")); + newTabAction->setEnabled(!link.isEmpty() && link.isValid()); + + menu.addSeparator(); + + QAction *selectAllAction = menu.addAction(tr("Select All") + + QString(QLatin1String("\t") + QString(QKeySequence(Qt::CTRL | Qt::Key_A)))); + + QAction *usedAction = menu.exec(mapToGlobal(contextMenuEvent->pos())); + if (usedAction == copyAction) { + QTextCursor cursor = browser->textCursor(); + if (!cursor.isNull() && cursor.hasSelection()) { + QString selectedText = cursor.selectedText(); + QMimeData *data = new QMimeData(); + data->setText(selectedText); + QApplication::clipboard()->setMimeData(data); + } + } + else if (usedAction == copyAnchorAction) { + QApplication::clipboard()->setText(link.toString()); + } + else if (usedAction == newTabAction) { + emit requestShowLinkInNewTab(link); + } + else if (usedAction == selectAllAction) { + browser->selectAll(); + } +#else + point = resultWidget->mapFromGlobal(point); + QUrl link = resultWidget->linkAt(point); + if (link.isEmpty() || !link.isValid()) + return; + + QAction *curTab = menu.addAction(tr("Open Link")); + QAction *newTab = menu.addAction(tr("Open Link in New Tab")); + + QAction *action = menu.exec(mapToGlobal(contextMenuEvent->pos())); + if (curTab == action) + emit requestShowLink(link); + else if (newTab == action) + emit requestShowLinkInNewTab(link); +#endif +} diff --git a/src/plugins/help/searchwidget.h b/src/plugins/help/searchwidget.h new file mode 100644 index 0000000000..64143fafbf --- /dev/null +++ b/src/plugins/help/searchwidget.h @@ -0,0 +1,88 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#ifndef SEARCHWIDGET_H +#define SEARCHWIDGET_H + +#include <QtCore/QUrl> +#include <QtCore/QPoint> + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QMouseEvent; +class QHelpSearchEngine; +class QHelpSearchResultWidget; + +QT_END_NAMESPACE + +namespace Help { +namespace Internal { + +class SearchWidget : public QWidget +{ + Q_OBJECT + +public: + SearchWidget(QHelpSearchEngine *engine, QWidget *parent = 0); + ~SearchWidget(); + + void zoomIn(); + void zoomOut(); + void resetZoom(); + +signals: + void requestShowLink(const QUrl &url); + void requestShowLinkInNewTab(const QUrl &url); + void escapePressed(); + +private slots: + void search() const; + void searchingStarted(); + void searchingFinished(int hits); + +private: + void keyPressEvent(QKeyEvent *keyEvent); + void contextMenuEvent(QContextMenuEvent *contextMenuEvent); + +private: + int zoomCount; + QHelpSearchEngine *searchEngine; + QHelpSearchResultWidget *resultWidget; +}; + +} // namespace Internal +} // namespace Help + +#endif // SEARCHWIDGET_H |