diff options
Diffstat (limited to 'src/assistant/tools/assistant')
127 files changed, 15846 insertions, 0 deletions
diff --git a/src/assistant/tools/assistant/Info_mac.plist b/src/assistant/tools/assistant/Info_mac.plist new file mode 100644 index 000000000..76369a1c5 --- /dev/null +++ b/src/assistant/tools/assistant/Info_mac.plist @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> +<plist version="0.9"> +<dict> + <key>CFBundleIconFile</key> + <string>@ICON@</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleGetInfoString</key> + <string>Created by Qt/QMake</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleIdentifier</key> + <string>com.trolltech.assistant</string> + <key>CFBundleExecutable</key> + <string>@EXECUTABLE@</string> +</dict> +</plist> diff --git a/src/assistant/tools/assistant/aboutdialog.cpp b/src/assistant/tools/assistant/aboutdialog.cpp new file mode 100644 index 000000000..b4c390e90 --- /dev/null +++ b/src/assistant/tools/assistant/aboutdialog.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "aboutdialog.h" + +#include "helpviewer.h" +#include "tracer.h" + +#include <QtCore/QBuffer> + +#include <QtGui/QLabel> +#include <QtGui/QPushButton> +#include <QtGui/QLayout> +#include <QtGui/QApplication> +#include <QtGui/QDesktopWidget> +#include <QtGui/QMessageBox> +#include <QtGui/QDesktopServices> + +QT_BEGIN_NAMESPACE + +AboutLabel::AboutLabel(QWidget *parent) + : QTextBrowser(parent) +{ + TRACE_OBJ + setFrameStyle(QFrame::NoFrame); + QPalette p; + p.setColor(QPalette::Base, p.color(QPalette::Background)); + setPalette(p); +} + +void AboutLabel::setText(const QString &text, const QByteArray &resources) +{ + TRACE_OBJ + QDataStream in(resources); + in >> m_resourceMap; + + QTextBrowser::setText(text); +} + +QSize AboutLabel::minimumSizeHint() const +{ + TRACE_OBJ + QTextDocument *doc = document(); + doc->adjustSize(); + return QSize(int(doc->size().width()), int(doc->size().height())); +} + +QVariant AboutLabel::loadResource(int type, const QUrl &name) +{ + TRACE_OBJ + if (type == 2 || type == 3) { + if (m_resourceMap.contains(name.toString())) { + return m_resourceMap.value(name.toString()); + } + } + return QVariant(); +} + +void AboutLabel::setSource(const QUrl &url) +{ + TRACE_OBJ + if (url.isValid() && (!HelpViewer::isLocalUrl(url) + || !HelpViewer::canOpenPage(url.path()))) { + if (!QDesktopServices::openUrl(url)) { + QMessageBox::warning(this, tr("Warning"), + tr("Unable to launch external application.\n"), tr("OK")); + } + } +} + +AboutDialog::AboutDialog(QWidget *parent) + : QDialog(parent, Qt::MSWindowsFixedSizeDialogHint | + Qt::WindowTitleHint|Qt::WindowSystemMenuHint) +{ + TRACE_OBJ + m_pixmapLabel = 0; + m_aboutLabel = new AboutLabel(); + + m_closeButton = new QPushButton(); + m_closeButton->setText(tr("&Close")); + connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close())); + + m_layout = new QGridLayout(this); + m_layout->addWidget(m_aboutLabel, 1, 0, 1, -1); + m_layout->addItem(new QSpacerItem(20, 10, QSizePolicy::Minimum, + QSizePolicy::Fixed), 2, 1, 1, 1); + m_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding), 3, 0, 1, 1); + m_layout->addWidget(m_closeButton, 3, 1, 1, 1); + m_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding), 3, 2, 1, 1); +} + +void AboutDialog::setText(const QString &text, const QByteArray &resources) +{ + TRACE_OBJ + m_aboutLabel->setText(text, resources); + updateSize(); +} + +void AboutDialog::setPixmap(const QPixmap &pixmap) +{ + TRACE_OBJ + if (!m_pixmapLabel) { + m_pixmapLabel = new QLabel(); + m_layout->addWidget(m_pixmapLabel, 0, 0, 1, -1, Qt::AlignCenter); + } + m_pixmapLabel->setPixmap(pixmap); + updateSize(); +} + +QString AboutDialog::documentTitle() const +{ + TRACE_OBJ + return m_aboutLabel->documentTitle(); +} + +void AboutDialog::updateSize() +{ + TRACE_OBJ + QSize screenSize = QApplication::desktop()->availableGeometry(QCursor::pos()) + .size(); + int limit = qMin(screenSize.width()/2, 500); + +#ifdef Q_WS_MAC + limit = qMin(screenSize.width()/2, 420); +#endif + + layout()->activate(); + int width = layout()->totalMinimumSize().width(); + + if (width > limit) + width = limit; + + QFontMetrics fm(qApp->font("QWorkspaceTitleBar")); + int windowTitleWidth = qMin(fm.width(windowTitle()) + 50, limit); + if (windowTitleWidth > width) + width = windowTitleWidth; + + layout()->activate(); + int height = (layout()->hasHeightForWidth()) + ? layout()->totalHeightForWidth(width) + : layout()->totalMinimumSize().height(); + setFixedSize(width, height); + QCoreApplication::removePostedEvents(this, QEvent::LayoutRequest); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/aboutdialog.h b/src/assistant/tools/assistant/aboutdialog.h new file mode 100644 index 000000000..292312d05 --- /dev/null +++ b/src/assistant/tools/assistant/aboutdialog.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ABOUTDIALOG_H +#define ABOUTDIALOG_H + +#include <QtGui/QTextBrowser> +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QLabel; +class QPushButton; +class QGridLayout; + +class AboutLabel : public QTextBrowser +{ + Q_OBJECT + +public: + AboutLabel(QWidget *parent = 0); + void setText(const QString &text, const QByteArray &resources); + QSize minimumSizeHint() const; + +private: + QVariant loadResource(int type, const QUrl &name); + void setSource(const QUrl &url); + + QMap<QString, QByteArray> m_resourceMap; +}; + +class AboutDialog : public QDialog +{ + Q_OBJECT + +public: + AboutDialog(QWidget *parent = 0); + void setText(const QString &text, const QByteArray &resources); + void setPixmap(const QPixmap &pixmap); + QString documentTitle() const; + +private: + void updateSize(); + + QLabel *m_pixmapLabel; + AboutLabel *m_aboutLabel; + QPushButton *m_closeButton; + QGridLayout *m_layout; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/assistant/tools/assistant/assistant.icns b/src/assistant/tools/assistant/assistant.icns Binary files differnew file mode 100644 index 000000000..6291dd397 --- /dev/null +++ b/src/assistant/tools/assistant/assistant.icns diff --git a/src/assistant/tools/assistant/assistant.ico b/src/assistant/tools/assistant/assistant.ico Binary files differnew file mode 100644 index 000000000..9e1b83f1b --- /dev/null +++ b/src/assistant/tools/assistant/assistant.ico diff --git a/src/assistant/tools/assistant/assistant.pro b/src/assistant/tools/assistant/assistant.pro new file mode 100644 index 000000000..7f0fdd158 --- /dev/null +++ b/src/assistant/tools/assistant/assistant.pro @@ -0,0 +1,118 @@ +include(../../../shared/fontpanel/fontpanel.pri) +TEMPLATE = app +LANGUAGE = C++ +TARGET = assistant +contains(QT_CONFIG, webkit):QT += webkit +CONFIG += qt \ + warn_on \ + help +QT += network +PROJECTNAME = Assistant +DESTDIR = ../../../../bin +target.path = $$[QT_INSTALL_BINS] +INSTALLS += target +DEPENDPATH += ../shared +DEPENDPATH += . +INCLUDEPATH += . + +# ## Work around a qmake issue when statically linking to +# ## not-yet-installed plugins +QMAKE_LIBDIR += $$QT_BUILD_TREE/plugins/sqldrivers +HEADERS += aboutdialog.h \ + bookmarkdialog.h \ + bookmarkfiltermodel.h \ + bookmarkitem.h \ + bookmarkmanager.h \ + bookmarkmanagerwidget.h \ + bookmarkmodel.h \ + centralwidget.h \ + cmdlineparser.h \ + contentwindow.h \ + findwidget.h \ + filternamedialog.h \ + helpenginewrapper.h \ + helpviewer.h \ + helpviewer_p.h \ + indexwindow.h \ + installdialog.h \ + mainwindow.h \ + preferencesdialog.h \ + qtdocinstaller.h \ + remotecontrol.h \ + searchwidget.h \ + topicchooser.h \ + tracer.h \ + xbelsupport.h \ + ../shared/collectionconfiguration.h \ + openpagesmodel.h \ + globalactions.h \ + openpageswidget.h \ + openpagesmanager.h \ + openpagesswitcher.h +win32:HEADERS += remotecontrol_win.h + +SOURCES += aboutdialog.cpp \ + bookmarkdialog.cpp \ + bookmarkfiltermodel.cpp \ + bookmarkitem.cpp \ + bookmarkmanager.cpp \ + bookmarkmanagerwidget.cpp \ + bookmarkmodel.cpp \ + centralwidget.cpp \ + cmdlineparser.cpp \ + contentwindow.cpp \ + findwidget.cpp \ + filternamedialog.cpp \ + helpenginewrapper.cpp \ + helpviewer.cpp \ + indexwindow.cpp \ + installdialog.cpp \ + main.cpp \ + mainwindow.cpp \ + preferencesdialog.cpp \ + qtdocinstaller.cpp \ + remotecontrol.cpp \ + searchwidget.cpp \ + topicchooser.cpp \ + xbelsupport.cpp \ + ../shared/collectionconfiguration.cpp \ + openpagesmodel.cpp \ + globalactions.cpp \ + openpageswidget.cpp \ + openpagesmanager.cpp \ + openpagesswitcher.cpp +contains(QT_CONFIG, webkit) { + SOURCES += helpviewer_qwv.cpp +} else { + SOURCES += helpviewer_qtb.cpp +} + +FORMS += bookmarkdialog.ui \ + bookmarkmanagerwidget.ui \ + bookmarkwidget.ui \ + filternamedialog.ui \ + installdialog.ui \ + preferencesdialog.ui \ + topicchooser.ui + +RESOURCES += assistant.qrc \ + assistant_images.qrc + +win32 { + !wince*:LIBS += -lshell32 + RC_FILE = assistant.rc +} + +mac { + ICON = assistant.icns + TARGET = Assistant + QMAKE_INFO_PLIST = Info_mac.plist +} + +contains(CONFIG, static): { + SQLPLUGINS = $$unique(sql-plugins) + contains(SQLPLUGINS, sqlite): { + QTPLUGIN += qsqlite + DEFINES += USE_STATIC_SQLITE_PLUGIN + } +} diff --git a/src/assistant/tools/assistant/assistant.qch b/src/assistant/tools/assistant/assistant.qch Binary files differnew file mode 100644 index 000000000..e6d52997b --- /dev/null +++ b/src/assistant/tools/assistant/assistant.qch diff --git a/src/assistant/tools/assistant/assistant.qrc b/src/assistant/tools/assistant/assistant.qrc new file mode 100644 index 000000000..dddf1be75 --- /dev/null +++ b/src/assistant/tools/assistant/assistant.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/trolltech/assistant" > + <file>assistant.qch</file> + </qresource> +</RCC> diff --git a/src/assistant/tools/assistant/assistant.rc b/src/assistant/tools/assistant/assistant.rc new file mode 100644 index 000000000..b1bf97b5c --- /dev/null +++ b/src/assistant/tools/assistant/assistant.rc @@ -0,0 +1,32 @@ +#include "winver.h" + +IDI_ICON1 ICON DISCARDABLE "assistant.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGS 0x0L + FILEFLAGSMASK 0x3fL + FILEOS 0x00040004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "Nokia Corporation and/or its subsidiary(-ies)" + VALUE "FileDescription", "Qt Assistant" + VALUE "FileVersion", "1.0.0.0" + VALUE "LegalCopyright", "Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)." + VALUE "InternalName", "assistant.exe" + VALUE "OriginalFilename", "assistant.exe" + VALUE "ProductName", "Qt Assistant" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/src/assistant/tools/assistant/assistant_images.qrc b/src/assistant/tools/assistant/assistant_images.qrc new file mode 100644 index 000000000..b4f25236d --- /dev/null +++ b/src/assistant/tools/assistant/assistant_images.qrc @@ -0,0 +1,37 @@ +<RCC> + <qresource prefix="/trolltech/assistant"> + <file>images/trolltech-logo.png</file> + <file>images/assistant-128.png</file> + <file>images/assistant.png</file> + <file>images/wrap.png</file> + <file>images/bookmark.png</file> + <file>images/mac/addtab.png</file> + <file>images/mac/book.png</file> + <file>images/mac/closetab.png</file> + <file>images/mac/editcopy.png</file> + <file>images/mac/find.png</file> + <file>images/mac/home.png</file> + <file>images/mac/next.png</file> + <file>images/mac/previous.png</file> + <file>images/mac/print.png</file> + <file>images/mac/synctoc.png</file> + <file>images/mac/zoomin.png</file> + <file>images/mac/zoomout.png</file> + <file>images/mac/resetzoom.png</file> + <file>images/win/addtab.png</file> + <file>images/win/book.png</file> + <file>images/win/closetab.png</file> + <file>images/win/editcopy.png</file> + <file>images/win/find.png</file> + <file>images/win/home.png</file> + <file>images/win/next.png</file> + <file>images/win/previous.png</file> + <file>images/win/print.png</file> + <file>images/win/synctoc.png</file> + <file>images/win/zoomin.png</file> + <file>images/win/zoomout.png</file> + <file>images/win/resetzoom.png</file> + <file>images/closebutton.png</file> + <file>images/darkclosebutton.png</file> + </qresource> +</RCC> diff --git a/src/assistant/tools/assistant/bookmarkdialog.cpp b/src/assistant/tools/assistant/bookmarkdialog.cpp new file mode 100644 index 000000000..d2f88f968 --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkdialog.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "bookmarkdialog.h" +#include "bookmarkfiltermodel.h" +#include "bookmarkitem.h" +#include "bookmarkmodel.h" +#include "helpenginewrapper.h" +#include "tracer.h" + +#include <QtGui/QKeyEvent> +#include <QtGui/QMenu> + +QT_BEGIN_NAMESPACE + +BookmarkDialog::BookmarkDialog(BookmarkModel *sourceModel, const QString &title, + const QString &url, QWidget *parent) + : QDialog(parent) + , m_url(url) + , m_title(title) + , bookmarkModel(sourceModel) +{ + TRACE_OBJ + ui.setupUi(this); + + ui.bookmarkEdit->setText(m_title); + ui.newFolderButton->setVisible(false); + ui.buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(accepted())); + connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(rejected())); + connect(ui.newFolderButton, SIGNAL(clicked()), this, SLOT(addFolder())); + connect(ui.toolButton, SIGNAL(clicked()), this, SLOT(toolButtonClicked())); + connect(ui.bookmarkEdit, SIGNAL(textChanged(QString)), this, + SLOT(textChanged(QString))); + + bookmarkProxyModel = new BookmarkFilterModel(this); + bookmarkProxyModel->setSourceModel(bookmarkModel); + ui.bookmarkFolders->setModel(bookmarkProxyModel); + connect(ui.bookmarkFolders, SIGNAL(currentIndexChanged(int)), this, + SLOT(currentIndexChanged(int))); + + bookmarkTreeModel = new BookmarkTreeModel(this); + bookmarkTreeModel->setSourceModel(bookmarkModel); + ui.treeView->setModel(bookmarkTreeModel); + + ui.treeView->expandAll(); + ui.treeView->setVisible(false); + ui.treeView->installEventFilter(this); + ui.treeView->viewport()->installEventFilter(this); + ui.treeView->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(ui.treeView, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(customContextMenuRequested(QPoint))); + connect(ui.treeView->selectionModel(), SIGNAL(currentChanged(QModelIndex, + QModelIndex)), this, SLOT(currentIndexChanged(QModelIndex))); + + ui.bookmarkFolders->setCurrentIndex(0); + ui.treeView->setCurrentIndex(ui.treeView->indexAt(QPoint(2, 2))); + + const HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + if (helpEngine.usesAppFont()) + setFont(helpEngine.appFont()); +} + +BookmarkDialog::~BookmarkDialog() +{ + TRACE_OBJ +} + +bool BookmarkDialog::isRootItem(const QModelIndex &index) const +{ + return !bookmarkTreeModel->parent(index).isValid(); +} + +bool BookmarkDialog::eventFilter(QObject *object, QEvent *event) +{ + TRACE_OBJ + if (object != ui.treeView && object != ui.treeView->viewport()) + return QWidget::eventFilter(object, event); + + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + switch (ke->key()) { + case Qt::Key_F2: { + const QModelIndex &index = ui.treeView->currentIndex(); + if (!isRootItem(index)) { + bookmarkModel->setItemsEditable(true); + ui.treeView->edit(index); + bookmarkModel->setItemsEditable(false); + } + } break; + default: break; + } + } + + return QObject::eventFilter(object, event); +} + +void BookmarkDialog::currentIndexChanged(int row) +{ + TRACE_OBJ + QModelIndex next = bookmarkProxyModel->index(row, 0, QModelIndex()); + if (next.isValid()) { + next = bookmarkProxyModel->mapToSource(next); + ui.treeView->setCurrentIndex(bookmarkTreeModel->mapFromSource(next)); + } +} + +void BookmarkDialog::currentIndexChanged(const QModelIndex &index) +{ + TRACE_OBJ + const QModelIndex current = bookmarkTreeModel->mapToSource(index); + if (current.isValid()) { + const int row = bookmarkProxyModel->mapFromSource(current).row(); + ui.bookmarkFolders->setCurrentIndex(row); + } +} + +void BookmarkDialog::accepted() +{ + TRACE_OBJ + QModelIndex index = ui.treeView->currentIndex(); + if (index.isValid()) { + index = bookmarkModel->addItem(bookmarkTreeModel->mapToSource(index)); + bookmarkModel->setData(index, DataVector() << m_title << m_url << false); + } else + rejected(); + + accept(); +} + +void BookmarkDialog::rejected() +{ + TRACE_OBJ + foreach (const QPersistentModelIndex &index, cache) + bookmarkModel->removeItem(index); + reject(); +} + +void BookmarkDialog::addFolder() +{ + TRACE_OBJ + QModelIndex index = ui.treeView->currentIndex(); + if (index.isValid()) { + index = bookmarkModel->addItem(bookmarkTreeModel->mapToSource(index), + true); + cache.append(index); + + index = bookmarkTreeModel->mapFromSource(index); + if (index.isValid()) { + bookmarkModel->setItemsEditable(true); + ui.treeView->edit(index); + ui.treeView->expand(index); + ui.treeView->setCurrentIndex(index); + bookmarkModel->setItemsEditable(false); + } + } +} + +void BookmarkDialog::toolButtonClicked() +{ + TRACE_OBJ + const bool visible = !ui.treeView->isVisible(); + ui.treeView->setVisible(visible); + ui.newFolderButton->setVisible(visible); + + if (visible) { + resize(QSize(width(), 400)); + ui.toolButton->setText(QLatin1String("-")); + } else { + resize(width(), minimumHeight()); + ui.toolButton->setText(QLatin1String("+")); + } +} + +void BookmarkDialog::textChanged(const QString& text) +{ + m_title = text; +} + +void BookmarkDialog::customContextMenuRequested(const QPoint &point) +{ + TRACE_OBJ + const QModelIndex &index = ui.treeView->currentIndex(); + if (isRootItem(index)) + return; // check if we go to rename the "Bookmarks Menu", bail + + QMenu menu(QLatin1String(""), this); + QAction *renameItem = menu.addAction(tr("Rename Folder")); + + QAction *picked = menu.exec(ui.treeView->mapToGlobal(point)); + if (picked == renameItem) { + bookmarkModel->setItemsEditable(true); + ui.treeView->edit(index); + bookmarkModel->setItemsEditable(false); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/bookmarkdialog.h b/src/assistant/tools/assistant/bookmarkdialog.h new file mode 100644 index 000000000..dfa65bbf8 --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkdialog.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef BOOKMARKDIALOG_H +#define BOOKMARKDIALOG_H + +#include "ui_bookmarkdialog.h" + +QT_BEGIN_NAMESPACE + +class BookmarkModel; +class BookmarkFilterModel; +class BookmarkTreeModel; + +class BookmarkDialog : public QDialog +{ + Q_OBJECT +public: + BookmarkDialog(BookmarkModel *bookmarkModel, const QString &title, + const QString &url, QWidget *parent = 0); + ~BookmarkDialog(); + +private: + bool isRootItem(const QModelIndex &index) const; + bool eventFilter(QObject *object, QEvent *event); + +private slots: + void currentIndexChanged(int index); + void currentIndexChanged(const QModelIndex &index); + + void accepted(); + void rejected(); + + void addFolder(); + void toolButtonClicked(); + void textChanged(const QString& text); + void customContextMenuRequested(const QPoint &point); + +private: + QString m_url; + QString m_title; + Ui::BookmarkDialog ui; + QList<QPersistentModelIndex> cache; + + BookmarkModel *bookmarkModel; + BookmarkTreeModel *bookmarkTreeModel; + BookmarkFilterModel *bookmarkProxyModel; +}; + +QT_END_NAMESPACE + +#endif // BOOKMARKDIALOG_H diff --git a/src/assistant/tools/assistant/bookmarkdialog.ui b/src/assistant/tools/assistant/bookmarkdialog.ui new file mode 100644 index 000000000..51315317b --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkdialog.ui @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BookmarkDialog</class> + <widget class="QDialog" name="BookmarkDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>450</width> + <height>133</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Add Bookmark</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Bookmark:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Add in Folder:</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLineEdit" name="bookmarkEdit"/> + </item> + <item> + <widget class="QComboBox" name="bookmarkFolders"/> + </item> + </layout> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QToolButton" name="toolButton"> + <property name="minimumSize"> + <size> + <width>25</width> + <height>20</height> + </size> + </property> + <property name="text"> + <string>+</string> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="treeView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Ignored"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="headerHidden"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QPushButton" name="newFolderButton"> + <property name="text"> + <string>New Folder</string> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>BookmarkDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>BookmarkDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/assistant/tools/assistant/bookmarkfiltermodel.cpp b/src/assistant/tools/assistant/bookmarkfiltermodel.cpp new file mode 100644 index 000000000..412e08e5d --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkfiltermodel.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "bookmarkfiltermodel.h" + +#include "bookmarkitem.h" +#include "bookmarkmodel.h" + +BookmarkFilterModel::BookmarkFilterModel(QObject *parent) + : QAbstractProxyModel(parent) + , hideBookmarks(true) + , sourceModel(0) +{ +} + +void BookmarkFilterModel::setSourceModel(QAbstractItemModel *_sourceModel) +{ + beginResetModel(); + + if (sourceModel) { + disconnect(sourceModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), + this, SLOT(changed(QModelIndex, QModelIndex))); + disconnect(sourceModel, SIGNAL(rowsInserted(QModelIndex, int, int)), + this, SLOT(rowsInserted(QModelIndex, int, int))); + disconnect(sourceModel, + SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, + SLOT(rowsAboutToBeRemoved(QModelIndex, int, int))); + disconnect(sourceModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), + this, SLOT(rowsRemoved(QModelIndex, int, int))); + disconnect(sourceModel, SIGNAL(layoutAboutToBeChanged()), this, + SLOT(layoutAboutToBeChanged())); + disconnect(sourceModel, SIGNAL(layoutChanged()), this, + SLOT(layoutChanged())); + disconnect(sourceModel, SIGNAL(modelAboutToBeReset()), this, + SLOT(modelAboutToBeReset())); + disconnect(sourceModel, SIGNAL(modelReset()), this, SLOT(modelReset())); + } + + QAbstractProxyModel::setSourceModel(sourceModel); + sourceModel = qobject_cast<BookmarkModel*> (_sourceModel); + + connect(sourceModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, + SLOT(changed(QModelIndex, QModelIndex))); + + connect(sourceModel, SIGNAL(rowsInserted(QModelIndex, int, int)), + this, SLOT(rowsInserted(QModelIndex, int, int))); + + connect(sourceModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), + this, SLOT(rowsAboutToBeRemoved(QModelIndex, int, int))); + connect(sourceModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, + SLOT(rowsRemoved(QModelIndex, int, int))); + + connect(sourceModel, SIGNAL(layoutAboutToBeChanged()), this, + SLOT(layoutAboutToBeChanged())); + connect(sourceModel, SIGNAL(layoutChanged()), this, + SLOT(layoutChanged())); + + connect(sourceModel, SIGNAL(modelAboutToBeReset()), this, + SLOT(modelAboutToBeReset())); + connect(sourceModel, SIGNAL(modelReset()), this, SLOT(modelReset())); + + if (sourceModel) + setupCache(sourceModel->index(0, 0, QModelIndex()).parent()); + + endResetModel(); +} + +int BookmarkFilterModel::rowCount(const QModelIndex &index) const +{ + Q_UNUSED(index) + return cache.count(); +} + +int BookmarkFilterModel::columnCount(const QModelIndex &index) const +{ + Q_UNUSED(index) + if (sourceModel) + return sourceModel->columnCount(); + return 0; +} + +QModelIndex BookmarkFilterModel::mapToSource(const QModelIndex &proxyIndex) const +{ + const int row = proxyIndex.row(); + if (proxyIndex.isValid() && row >= 0 && row < cache.count()) + return cache[row]; + return QModelIndex(); +} + +QModelIndex BookmarkFilterModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + return index(cache.indexOf(sourceIndex), 0, QModelIndex()); +} + +QModelIndex BookmarkFilterModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child) + return QModelIndex(); +} + +QModelIndex BookmarkFilterModel::index(int row, int column, + const QModelIndex &index) const +{ + Q_UNUSED(index) + if (row < 0 || column < 0 || cache.count() <= row + || !sourceModel || sourceModel->columnCount() <= column) { + return QModelIndex(); + } + return createIndex(row, 0); +} + +Qt::DropActions BookmarkFilterModel::supportedDropActions () const +{ + if (sourceModel) + return sourceModel->supportedDropActions(); + return Qt::IgnoreAction; +} + +Qt::ItemFlags BookmarkFilterModel::flags(const QModelIndex &index) const +{ + if (sourceModel) + return sourceModel->flags(index); + return Qt::NoItemFlags; +} + +QVariant BookmarkFilterModel::data(const QModelIndex &index, int role) const +{ + if (sourceModel) + return sourceModel->data(mapToSource(index), role); + return QVariant(); +} + +bool BookmarkFilterModel::setData(const QModelIndex &index, const QVariant &value, + int role) +{ + if (sourceModel) + return sourceModel->setData(mapToSource(index), value, role); + return false; +} + +void BookmarkFilterModel::filterBookmarks() +{ + if (sourceModel) { + beginResetModel(); + hideBookmarks = true; + setupCache(sourceModel->index(0, 0, QModelIndex()).parent()); + endResetModel(); + } +} + +void BookmarkFilterModel::filterBookmarkFolders() +{ + if (sourceModel) { + beginResetModel(); + hideBookmarks = false; + setupCache(sourceModel->index(0, 0, QModelIndex()).parent()); + endResetModel(); + } +} + +void BookmarkFilterModel::changed(const QModelIndex &topLeft, + const QModelIndex &bottomRight) +{ + emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight)); +} + +void BookmarkFilterModel::rowsInserted(const QModelIndex &parent, int start, + int end) +{ + if (!sourceModel) + return; + + QModelIndex cachePrevious = parent; + if (BookmarkItem *parentItem = sourceModel->itemFromIndex(parent)) { + BookmarkItem *newItem = parentItem->child(start); + + // iterate over tree hirarchie to find the previous folder + for (int i = 0; i < parentItem->childCount(); ++i) { + if (BookmarkItem *child = parentItem->child(i)) { + const QModelIndex &tmp = sourceModel->indexFromItem(child); + if (tmp.data(UserRoleFolder).toBool() && child != newItem) + cachePrevious = tmp; + } + } + + const QModelIndex &newIndex = sourceModel->indexFromItem(newItem); + const bool isFolder = newIndex.data(UserRoleFolder).toBool(); + if ((isFolder && hideBookmarks) || (!isFolder && !hideBookmarks)) { + beginInsertRows(mapFromSource(parent), start, end); + const int index = cache.indexOf(cachePrevious) + 1; + if (cache.value(index, QPersistentModelIndex()) != newIndex) + cache.insert(index, newIndex); + endInsertRows(); + } + } +} + +void BookmarkFilterModel::rowsAboutToBeRemoved(const QModelIndex &parent, + int start, int end) +{ + if (!sourceModel) + return; + + if (BookmarkItem *parentItem = sourceModel->itemFromIndex(parent)) { + if (BookmarkItem *child = parentItem->child(start)) { + indexToRemove = sourceModel->indexFromItem(child); + if (cache.contains(indexToRemove)) + beginRemoveRows(mapFromSource(parent), start, end); + } + } +} + +void BookmarkFilterModel::rowsRemoved(const QModelIndex &/*parent*/, int, int) +{ + if (cache.contains(indexToRemove)) { + cache.removeAll(indexToRemove); + endRemoveRows(); + } +} + +void BookmarkFilterModel::layoutAboutToBeChanged() +{ + // TODO: ??? +} + +void BookmarkFilterModel::layoutChanged() +{ + // TODO: ??? +} + +void BookmarkFilterModel::modelAboutToBeReset() +{ + beginResetModel(); +} + +void BookmarkFilterModel::modelReset() +{ + if (sourceModel) + setupCache(sourceModel->index(0, 0, QModelIndex()).parent()); + endResetModel(); +} + +void BookmarkFilterModel::setupCache(const QModelIndex &parent) +{ + cache.clear(); + for (int i = 0; i < sourceModel->rowCount(parent); ++i) + collectItems(sourceModel->index(i, 0, parent)); +} + +void BookmarkFilterModel::collectItems(const QModelIndex &parent) +{ + if (parent.isValid()) { + bool isFolder = sourceModel->data(parent, UserRoleFolder).toBool(); + if ((isFolder && hideBookmarks) || (!isFolder && !hideBookmarks)) + cache.append(parent); + + if (sourceModel->hasChildren(parent)) { + for (int i = 0; i < sourceModel->rowCount(parent); ++i) + collectItems(sourceModel->index(i, 0, parent)); + } + } +} + +// -- BookmarkTreeModel + +BookmarkTreeModel::BookmarkTreeModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +int BookmarkTreeModel::columnCount(const QModelIndex &parent) const +{ + return qMin(1, QSortFilterProxyModel::columnCount(parent)); +} + +bool BookmarkTreeModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + Q_UNUSED(row) + BookmarkModel *model = qobject_cast<BookmarkModel*> (sourceModel()); + if (model->rowCount(parent) > 0 + && model->data(model->index(row, 0, parent), UserRoleFolder).toBool()) + return true; + return false; +} diff --git a/src/assistant/tools/assistant/bookmarkfiltermodel.h b/src/assistant/tools/assistant/bookmarkfiltermodel.h new file mode 100644 index 000000000..65ed12f20 --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkfiltermodel.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef BOOKMARKFILTERMODEL_H +#define BOOKMARKFILTERMODEL_H + +#include <QtCore/QPersistentModelIndex> + +#include <QtGui/QAbstractProxyModel> +#include <QtGui/QSortFilterProxyModel> + +QT_BEGIN_NAMESPACE + +class BookmarkItem; +class BookmarkModel; + +typedef QList<QPersistentModelIndex> PersistentModelIndexCache; + +class BookmarkFilterModel : public QAbstractProxyModel +{ + Q_OBJECT +public: + explicit BookmarkFilterModel(QObject *parent = 0); + + void setSourceModel(QAbstractItemModel *sourceModel); + + int rowCount(const QModelIndex &index) const; + int columnCount(const QModelIndex &index) const; + + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + + QModelIndex parent(const QModelIndex &child) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + + Qt::DropActions supportedDropActions () const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + QVariant data(const QModelIndex &index, int role) const; + bool setData(const QModelIndex &index, const QVariant &value, int role); + + void filterBookmarks(); + void filterBookmarkFolders(); + +private slots: + void changed(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void rowsInserted(const QModelIndex &parent, int start, int end); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex &parent, int start, int end); + void layoutAboutToBeChanged(); + void layoutChanged(); + void modelAboutToBeReset(); + void modelReset(); + +private: + void setupCache(const QModelIndex &parent); + void collectItems(const QModelIndex &parent); + +private: + bool hideBookmarks; + BookmarkModel *sourceModel; + PersistentModelIndexCache cache; + QPersistentModelIndex indexToRemove; +}; + +// -- BookmarkTreeModel + +class BookmarkTreeModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + BookmarkTreeModel(QObject *parent = 0); + int columnCount(const QModelIndex &parent = QModelIndex()) const; + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; +}; + +QT_END_NAMESPACE + +#endif // BOOKMARKFILTERMODEL_H diff --git a/src/assistant/tools/assistant/bookmarkitem.cpp b/src/assistant/tools/assistant/bookmarkitem.cpp new file mode 100644 index 000000000..8bcf451c1 --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkitem.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bookmarkitem.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +BookmarkItem::BookmarkItem(const DataVector &data, BookmarkItem *parent) + : m_data(data) + , m_parent(parent) +{ +} + +BookmarkItem::~BookmarkItem() +{ + qDeleteAll(m_children); +} + +BookmarkItem* +BookmarkItem::parent() const +{ + return m_parent; +} + +void +BookmarkItem::setParent(BookmarkItem *parent) +{ + m_parent = parent; +} + +void +BookmarkItem::addChild(BookmarkItem *child) +{ + child->setParent(this); + m_children.append(child); +} + +BookmarkItem* +BookmarkItem::child(int number) const +{ + if (number >= 0 && number < m_children.count()) + return m_children[number]; + return 0; +} + +int BookmarkItem::childCount() const +{ + return m_children.count(); +} + +int BookmarkItem::childNumber() const +{ + if (m_parent) + return m_parent->m_children.indexOf(const_cast<BookmarkItem*>(this)); + return 0; +} + +QVariant +BookmarkItem::data(int column) const +{ + if (column == 0) + return m_data[0]; + + if (column == 1 || column == UserRoleUrl) + return m_data[1]; + + if (column == UserRoleFolder) + return m_data[1].toString() == QLatin1String("Folder"); + + if (column == UserRoleExpanded) + return m_data[2]; + + return QVariant(); +} + +void +BookmarkItem::setData(const DataVector &data) +{ + m_data = data; +} + +bool +BookmarkItem::setData(int column, const QVariant &newValue) +{ + int index = -1; + if (column == 0 || column == 1) + index = column; + + if (column == UserRoleUrl || column == UserRoleFolder) + index = 1; + + if (column == UserRoleExpanded) + index = 2; + + if (index < 0) + return false; + + m_data[index] = newValue; + return true; +} + +bool +BookmarkItem::insertChildren(bool isFolder, int position, int count) +{ + if (position < 0 || position > m_children.size()) + return false; + + for (int row = 0; row < count; ++row) { + m_children.insert(position, new BookmarkItem(DataVector() + << (isFolder + ? QCoreApplication::translate("BookmarkItem", "New Folder") + : QCoreApplication::translate("BookmarkItem", "Untitled")) + << (isFolder ? "Folder" : "about:blank") << false, this)); + } + + return true; +} + +bool +BookmarkItem::removeChildren(int position, int count) +{ + if (position < 0 || position > m_children.size()) + return false; + + for (int row = 0; row < count; ++row) + delete m_children.takeAt(position); + + return true; +} + +void +BookmarkItem::dumpTree(int indent) const +{ + const QString tree(indent, ' '); + qDebug() << tree + (data(UserRoleFolder).toBool() ? "Folder" : "Bookmark") + << "Label:" << data(0).toString() << "parent:" << m_parent << "this:" + << this; + + foreach (BookmarkItem *item, m_children) + item->dumpTree(indent + 4); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/bookmarkitem.h b/src/assistant/tools/assistant/bookmarkitem.h new file mode 100644 index 000000000..96ce786be --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkitem.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef BOOKMARKITEM_H +#define BOOKMARKITEM_H + +#include <QtCore/QVariant> +#include <QtCore/QVector> + +QT_BEGIN_NAMESPACE + +enum { + UserRoleUrl = Qt::UserRole + 50, + UserRoleFolder = Qt::UserRole + 100, + UserRoleExpanded = Qt::UserRole + 150 +}; + +typedef QVector<QVariant> DataVector; + +class BookmarkItem +{ +public: + explicit BookmarkItem(const DataVector &data, BookmarkItem *parent = 0); + ~BookmarkItem(); + + BookmarkItem *parent() const; + void setParent(BookmarkItem *parent); + + void addChild(BookmarkItem *child); + BookmarkItem *child(int number) const; + + int childCount() const; + int childNumber() const; + + QVariant data(int column) const; + void setData(const DataVector &data); + bool setData(int column, const QVariant &value); + + bool insertChildren(bool isFolder, int position, int count); + bool removeChildren(int position, int count); + + void dumpTree(int indent) const; + +private: + DataVector m_data; + + BookmarkItem *m_parent; + QList<BookmarkItem*> m_children; +}; + +QT_END_NAMESPACE + +#endif // BOOKMARKITEM_H diff --git a/src/assistant/tools/assistant/bookmarkmanager.cpp b/src/assistant/tools/assistant/bookmarkmanager.cpp new file mode 100644 index 000000000..87331463b --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkmanager.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include "bookmarkmanager.h" +#include "bookmarkmanagerwidget.h" +#include "bookmarkdialog.h" +#include "bookmarkfiltermodel.h" +#include "bookmarkitem.h" +#include "bookmarkmodel.h" +#include "centralwidget.h" +#include "helpenginewrapper.h" + +#include <QtGui/QMenu> +#include <QtGui/QKeyEvent> +#include <QtGui/QMessageBox> +#include <QtGui/QSortFilterProxyModel> +#include <QtGui/QToolBar> + +QT_BEGIN_NAMESPACE + +// -- BookmarkManager::BookmarkWidget + +void BookmarkManager::BookmarkWidget::focusInEvent(QFocusEvent *event) +{ + TRACE_OBJ + if (event->reason() != Qt::MouseFocusReason) { + ui.lineEdit->selectAll(); + ui.lineEdit->setFocus(); + + // force the focus in event on bookmark manager + emit focusInEvent(); + } +} + +// -- BookmarkManager::BookmarkTreeView + +BookmarkManager::BookmarkTreeView::BookmarkTreeView(QWidget *parent) + : QTreeView(parent) +{ + TRACE_OBJ + setAcceptDrops(true); + setDragEnabled(true); + setAutoExpandDelay(1000); + setUniformRowHeights(true); + setDropIndicatorShown(true); + setExpandsOnDoubleClick(true); + + connect(this, SIGNAL(expanded(QModelIndex)), this, + SLOT(setExpandedData(QModelIndex))); + connect(this, SIGNAL(collapsed(QModelIndex)), this, + SLOT(setExpandedData(QModelIndex))); + +} + +void BookmarkManager::BookmarkTreeView::subclassKeyPressEvent(QKeyEvent *event) +{ + TRACE_OBJ + QTreeView::keyPressEvent(event); +} + +void BookmarkManager::BookmarkTreeView::setExpandedData(const QModelIndex &index) +{ + TRACE_OBJ + if (BookmarkModel *treeModel = qobject_cast<BookmarkModel*> (model())) + treeModel->setData(index, isExpanded(index), UserRoleExpanded); +} + +// -- BookmarkManager + +QMutex BookmarkManager::mutex; +BookmarkManager* BookmarkManager::bookmarkManager = 0; + +// -- public + +BookmarkManager* BookmarkManager::instance() +{ + TRACE_OBJ + if (!bookmarkManager) { + QMutexLocker _(&mutex); + if (!bookmarkManager) + bookmarkManager = new BookmarkManager(); + } + return bookmarkManager; +} + +void BookmarkManager::destroy() +{ + TRACE_OBJ + delete bookmarkManager; + bookmarkManager = 0; +} + +QWidget* BookmarkManager::bookmarkDockWidget() const +{ + TRACE_OBJ + if (bookmarkWidget) + return bookmarkWidget; + return 0; +} + +void BookmarkManager::setBookmarksMenu(QMenu* menu) +{ + TRACE_OBJ + bookmarkMenu = menu; + refreshBookmarkMenu(); +} + +void BookmarkManager::setBookmarksToolbar(QToolBar *toolBar) +{ + TRACE_OBJ + m_toolBar = toolBar; + refreshBookmarkToolBar(); +} + +// -- public slots + +void BookmarkManager::addBookmark(const QString &title, const QString &url) +{ + TRACE_OBJ + showBookmarkDialog(title.isEmpty() ? tr("Untitled") : title, + url.isEmpty() ? QLatin1String("about:blank") : url); +} + +// -- private + +BookmarkManager::BookmarkManager() + : typeAndSearch(false) + , bookmarkMenu(0) + , m_toolBar(0) + , bookmarkModel(new BookmarkModel) + , bookmarkFilterModel(0) + , typeAndSearchModel(0) + , bookmarkWidget(new BookmarkWidget) + , bookmarkTreeView(new BookmarkTreeView) + , bookmarkManagerWidget(0) +{ + TRACE_OBJ + bookmarkWidget->installEventFilter(this); + connect(bookmarkWidget->ui.add, SIGNAL(clicked()), this, + SLOT(addBookmark())); + connect(bookmarkWidget->ui.remove, SIGNAL(clicked()), this, + SLOT(removeBookmark())); + connect(bookmarkWidget->ui.lineEdit, SIGNAL(textChanged(QString)), this, + SLOT(textChanged(QString))); + connect(bookmarkWidget, SIGNAL(focusInEvent()), this, SLOT(focusInEvent())); + + bookmarkTreeView->setModel(bookmarkModel); + bookmarkTreeView->installEventFilter(this); + bookmarkTreeView->viewport()->installEventFilter(this); + bookmarkTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + bookmarkWidget->ui.stackedWidget->addWidget(bookmarkTreeView); + + connect(bookmarkTreeView, SIGNAL(activated(QModelIndex)), this, + SLOT(setSourceFromIndex(QModelIndex))); + connect(bookmarkTreeView, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(customContextMenuRequested(QPoint))); + + connect(&HelpEngineWrapper::instance(), SIGNAL(setupFinished()), this, + SLOT(setupFinished())); + connect(bookmarkModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, + SLOT(refreshBookmarkMenu())); + connect(bookmarkModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, + SLOT(refreshBookmarkMenu())); + connect(bookmarkModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, + SLOT(refreshBookmarkMenu())); + + connect(bookmarkModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, + SLOT(refreshBookmarkToolBar())); + connect(bookmarkModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, + SLOT(refreshBookmarkToolBar())); + connect(bookmarkModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, + SLOT(refreshBookmarkToolBar())); +} + +BookmarkManager::~BookmarkManager() +{ + TRACE_OBJ + delete bookmarkManagerWidget; + HelpEngineWrapper::instance().setBookmarks(bookmarkModel->bookmarks()); + delete bookmarkModel; +} + +void BookmarkManager::removeItem(const QModelIndex &index) +{ + TRACE_OBJ + QModelIndex current = index; + if (typeAndSearch) { // need to map because of proxy + current = typeAndSearchModel->mapToSource(current); + current = bookmarkFilterModel->mapToSource(current); + } else if (!bookmarkModel->parent(index).isValid()) { + return; // check if we should delete the "Bookmarks Menu", bail + } + + if (bookmarkModel->hasChildren(current)) { + int value = QMessageBox::question(bookmarkTreeView, tr("Remove"), + tr("You are going to delete a Folder, this will also<br>" + "remove it's content. Are you sure to continue?"), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + if (value == QMessageBox::Cancel) + return; + } + bookmarkModel->removeItem(current); +} + +bool BookmarkManager::eventFilter(QObject *object, QEvent *event) +{ + if (object != bookmarkTreeView && object != bookmarkTreeView->viewport() + && object != bookmarkWidget) + return QObject::eventFilter(object, event); + + TRACE_OBJ + const bool isWidget = object == bookmarkWidget; + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + switch (ke->key()) { + case Qt::Key_F2: { + renameBookmark(bookmarkTreeView->currentIndex()); + } break; + + case Qt::Key_Delete: { + removeItem(bookmarkTreeView->currentIndex()); + return true; + } break; + + case Qt::Key_Up: { // needs event filter on widget + case Qt::Key_Down: + if (isWidget) + bookmarkTreeView->subclassKeyPressEvent(ke); + } break; + + case Qt::Key_Escape: { + emit escapePressed(); + } break; + + default: break; + } + } + + if (event->type() == QEvent::MouseButtonRelease && !isWidget) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + switch (me->button()) { + case Qt::LeftButton: { + if (me->modifiers() & Qt::ControlModifier) + setSourceFromIndex(bookmarkTreeView->currentIndex(), true); + } break; + + case Qt::MidButton: { + setSourceFromIndex(bookmarkTreeView->currentIndex(), true); + } break; + + default: break; + } + } + + return QObject::eventFilter(object, event); +} + +void BookmarkManager::buildBookmarksMenu(const QModelIndex &index, QMenu* menu) +{ + TRACE_OBJ + if (!index.isValid()) + return; + + const QString &text = index.data().toString(); + const QIcon &icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole)); + if (index.data(UserRoleFolder).toBool()) { + if (QMenu* subMenu = menu->addMenu(icon, text)) { + for (int i = 0; i < bookmarkModel->rowCount(index); ++i) + buildBookmarksMenu(bookmarkModel->index(i, 0, index), subMenu); + } + } else { + QAction *action = menu->addAction(icon, text); + action->setData(index.data(UserRoleUrl).toString()); + } +} + +void BookmarkManager::showBookmarkDialog(const QString &name, const QString &url) +{ + TRACE_OBJ + BookmarkDialog dialog(bookmarkModel, name, url, bookmarkTreeView); + dialog.exec(); +} + +// -- private slots + +void BookmarkManager::setupFinished() +{ + TRACE_OBJ + bookmarkModel->setBookmarks(HelpEngineWrapper::instance().bookmarks()); + bookmarkModel->expandFoldersIfNeeeded(bookmarkTreeView); + + refreshBookmarkMenu(); + refreshBookmarkToolBar(); + + bookmarkTreeView->hideColumn(1); + bookmarkTreeView->header()->setVisible(false); + bookmarkTreeView->header()->setStretchLastSection(true); + + if (!bookmarkFilterModel) + bookmarkFilterModel = new BookmarkFilterModel(this); + bookmarkFilterModel->setSourceModel(bookmarkModel); + bookmarkFilterModel->filterBookmarkFolders(); + + if (!typeAndSearchModel) + typeAndSearchModel = new QSortFilterProxyModel(this); + typeAndSearchModel->setDynamicSortFilter(true); + typeAndSearchModel->setSourceModel(bookmarkFilterModel); +} + +void BookmarkManager::addBookmark() +{ + TRACE_OBJ + if (CentralWidget *widget = CentralWidget::instance()) + addBookmark(widget->currentTitle(), widget->currentSource().toString()); +} + +void BookmarkManager::removeBookmark() +{ + TRACE_OBJ + removeItem(bookmarkTreeView->currentIndex()); +} + +void BookmarkManager::manageBookmarks() +{ + TRACE_OBJ + if (bookmarkManagerWidget == 0) { + bookmarkManagerWidget = new BookmarkManagerWidget(bookmarkModel); + connect(bookmarkManagerWidget, SIGNAL(setSource(QUrl)), this, + SIGNAL(setSource(QUrl))); + connect(bookmarkManagerWidget, SIGNAL(setSourceInNewTab(QUrl)) + , this, SIGNAL(setSourceInNewTab(QUrl))); + connect(bookmarkManagerWidget, SIGNAL(managerWidgetAboutToClose()) + , this, SLOT(managerWidgetAboutToClose())); + } + bookmarkManagerWidget->show(); + bookmarkManagerWidget->raise(); +} + +void BookmarkManager::refreshBookmarkMenu() +{ + TRACE_OBJ + if (!bookmarkMenu) + return; + + bookmarkMenu->clear(); + + bookmarkMenu->addAction(tr("Manage Bookmarks..."), this, + SLOT(manageBookmarks())); + bookmarkMenu->addAction(QIcon::fromTheme("bookmark-new"), + tr("Add Bookmark..."), this, SLOT(addBookmark()), QKeySequence(tr("Ctrl+D"))); + + bookmarkMenu->addSeparator(); + + QModelIndex root = bookmarkModel->index(0, 0, QModelIndex()).parent(); + buildBookmarksMenu(bookmarkModel->index(0, 0, root), bookmarkMenu); + + bookmarkMenu->addSeparator(); + + root = bookmarkModel->index(1, 0, QModelIndex()); + for (int i = 0; i < bookmarkModel->rowCount(root); ++i) + buildBookmarksMenu(bookmarkModel->index(i, 0, root), bookmarkMenu); + + connect(bookmarkMenu, SIGNAL(triggered(QAction*)), this, + SLOT(setSourceFromAction(QAction*))); +} + +void BookmarkManager::refreshBookmarkToolBar() +{ + TRACE_OBJ + if (!m_toolBar) + return; + + m_toolBar->clear(); + m_toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + const QModelIndex &root = bookmarkModel->index(0, 0, QModelIndex()); + for (int i = 0; i < bookmarkModel->rowCount(root); ++i) { + const QModelIndex &index = bookmarkModel->index(i, 0, root); + if (index.data(UserRoleFolder).toBool()) { + QToolButton *button = new QToolButton(m_toolBar); + button->setPopupMode(QToolButton::InstantPopup); + button->setText(index.data().toString()); + QMenu *menu = new QMenu(button); + for (int j = 0; j < bookmarkModel->rowCount(index); ++j) + buildBookmarksMenu(bookmarkModel->index(j, 0, index), menu); + connect(menu, SIGNAL(triggered(QAction*)), this, + SLOT(setSourceFromAction(QAction*))); + button->setMenu(menu); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + button->setIcon(qvariant_cast<QIcon>(index.data(Qt::DecorationRole))); + QAction *a = m_toolBar->addWidget(button); + a->setText(index.data().toString()); + } else { + QAction *action = m_toolBar->addAction( + qvariant_cast<QIcon>(index.data(Qt::DecorationRole)), + index.data().toString(), this, SLOT(setSourceFromAction())); + action->setData(index.data(UserRoleUrl).toString()); + } + } +} + +void BookmarkManager::renameBookmark(const QModelIndex &index) +{ + // check if we should rename the "Bookmarks Menu", bail + if (!typeAndSearch && !bookmarkModel->parent(index).isValid()) + return; + + bookmarkModel->setItemsEditable(true); + bookmarkTreeView->edit(index); + bookmarkModel->setItemsEditable(false); +} + + +void BookmarkManager::setSourceFromAction() +{ + TRACE_OBJ + setSourceFromAction(qobject_cast<QAction*> (sender())); +} + +void BookmarkManager::setSourceFromAction(QAction *action) +{ + TRACE_OBJ + if (action) { + const QVariant &data = action->data(); + if (data.canConvert<QUrl>()) + emit setSource(data.toUrl()); + } +} + +void BookmarkManager::setSourceFromIndex(const QModelIndex &index, bool newTab) +{ + TRACE_OBJ + QAbstractItemModel *base = bookmarkModel; + if (typeAndSearch) + base = typeAndSearchModel; + + if (base->data(index, UserRoleFolder).toBool()) + return; + + const QVariant &data = base->data(index, UserRoleUrl); + if (data.canConvert<QUrl>()) { + if (newTab) + emit setSourceInNewTab(data.toUrl()); + else + emit setSource(data.toUrl()); + } +} + +void BookmarkManager::customContextMenuRequested(const QPoint &point) +{ + TRACE_OBJ + QModelIndex index = bookmarkTreeView->indexAt(point); + if (!index.isValid()) + return; + + // check if we should open the menu on "Bookmarks Menu", bail + if (!typeAndSearch && !bookmarkModel->parent(index).isValid()) + return; + + QAction *remove = 0; + QAction *rename = 0; + QAction *showItem = 0; + QAction *showItemInNewTab = 0; + + QMenu menu(QLatin1String("")); + if (!typeAndSearch && bookmarkModel->data(index, UserRoleFolder).toBool()) { + remove = menu.addAction(tr("Delete Folder")); + rename = menu.addAction(tr("Rename Folder")); + } else { + showItem = menu.addAction(tr("Show Bookmark")); + showItemInNewTab = menu.addAction(tr("Show Bookmark in New Tab")); + menu.addSeparator(); + remove = menu.addAction(tr("Delete Bookmark")); + rename = menu.addAction(tr("Rename Bookmark")); + } + + QAction *pickedAction = menu.exec(bookmarkTreeView->mapToGlobal(point)); + if (pickedAction == rename) + renameBookmark(index); + else if (pickedAction == remove) + removeItem(index); + else if (pickedAction == showItem || pickedAction == showItemInNewTab) + setSourceFromIndex(index, pickedAction == showItemInNewTab); +} + +void BookmarkManager::focusInEvent() +{ + TRACE_OBJ + const QModelIndex &index = bookmarkTreeView->indexAt(QPoint(2, 2)); + if (index.isValid()) + bookmarkTreeView->setCurrentIndex(index); +} + +void BookmarkManager::managerWidgetAboutToClose() +{ + delete bookmarkManagerWidget; + bookmarkManagerWidget = 0; +} + +void BookmarkManager::textChanged(const QString &text) +{ + TRACE_OBJ + if (!bookmarkWidget->ui.lineEdit->text().isEmpty()) { + if (!typeAndSearch) { + typeAndSearch = true; + bookmarkTreeView->setItemsExpandable(false); + bookmarkTreeView->setRootIsDecorated(false); + bookmarkTreeView->setModel(typeAndSearchModel); + } + typeAndSearchModel->setFilterRegExp(QRegExp(text)); + } else { + typeAndSearch = false; + bookmarkTreeView->setModel(bookmarkModel); + bookmarkTreeView->setItemsExpandable(true); + bookmarkTreeView->setRootIsDecorated(true); + bookmarkModel->expandFoldersIfNeeeded(bookmarkTreeView); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/bookmarkmanager.h b/src/assistant/tools/assistant/bookmarkmanager.h new file mode 100644 index 000000000..f5823a7fa --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkmanager.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef BOOKMARKMANAGER_H +#define BOOKMARKMANAGER_H + +#include <QtCore/QMutex> +#include <QtGui/QTreeView> + +#include "ui_bookmarkwidget.h" + +QT_BEGIN_NAMESPACE + +class BookmarkManagerWidget; +class BookmarkModel; +class BookmarkFilterModel; +class QKeyEvent; +class QSortFilterProxyModel; +class QToolBar; + +class BookmarkManager : public QObject +{ + Q_OBJECT + class BookmarkWidget; + class BookmarkTreeView; + class BookmarkListView; + Q_DISABLE_COPY(BookmarkManager); + +public: + static BookmarkManager* instance(); + static void destroy(); + + QWidget* bookmarkDockWidget() const; + void setBookmarksMenu(QMenu* menu); + void setBookmarksToolbar(QToolBar *toolBar); + +public slots: + void addBookmark(const QString &title, const QString &url); + +signals: + void escapePressed(); + void setSource(const QUrl &url); + void setSourceInNewTab(const QUrl &url); + +private: + BookmarkManager(); + ~BookmarkManager(); + + void removeItem(const QModelIndex &index); + bool eventFilter(QObject *object, QEvent *event); + void buildBookmarksMenu(const QModelIndex &index, QMenu *menu); + void showBookmarkDialog(const QString &name, const QString &url); + +private slots: + void setupFinished(); + + void addBookmark(); + void removeBookmark(); + void manageBookmarks(); + void refreshBookmarkMenu(); + void refreshBookmarkToolBar(); + void renameBookmark(const QModelIndex &index); + + void setSourceFromAction(); + void setSourceFromAction(QAction *action); + void setSourceFromIndex(const QModelIndex &index, bool newTab = false); + + void focusInEvent(); + void managerWidgetAboutToClose(); + void textChanged(const QString &text); + void customContextMenuRequested(const QPoint &point); + +private: + bool typeAndSearch; + + static QMutex mutex; + static BookmarkManager *bookmarkManager; + + QMenu *bookmarkMenu; + QToolBar *m_toolBar; + + BookmarkModel *bookmarkModel; + BookmarkFilterModel *bookmarkFilterModel; + QSortFilterProxyModel *typeAndSearchModel; + + BookmarkWidget *bookmarkWidget; + BookmarkTreeView *bookmarkTreeView; + BookmarkManagerWidget *bookmarkManagerWidget; +}; + +class BookmarkManager::BookmarkWidget : public QWidget +{ + Q_OBJECT +public: + BookmarkWidget(QWidget *parent = 0) + : QWidget(parent) { ui.setupUi(this); } + virtual ~BookmarkWidget() {} + + Ui::BookmarkWidget ui; + +signals: + void focusInEvent(); + +private: + void focusInEvent(QFocusEvent *event); +}; + +class BookmarkManager::BookmarkTreeView : public QTreeView +{ + Q_OBJECT +public: + BookmarkTreeView(QWidget *parent = 0); + ~BookmarkTreeView() {} + + void subclassKeyPressEvent(QKeyEvent *event); + +private slots: + void setExpandedData(const QModelIndex &index); +}; + +QT_END_NAMESPACE + +#endif // BOOKMARKMANAGER_H diff --git a/src/assistant/tools/assistant/bookmarkmanagerwidget.cpp b/src/assistant/tools/assistant/bookmarkmanagerwidget.cpp new file mode 100644 index 000000000..dd410d3a2 --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkmanagerwidget.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "bookmarkmanagerwidget.h" +#include "bookmarkitem.h" +#include "bookmarkmodel.h" +#include "tracer.h" +#include "xbelsupport.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QFile> +#include <QtCore/QUrl> + +#include <QtGui/QCloseEvent> +#include <QtGui/QFileDialog> +#include <QtGui/QKeySequence> +#include <QtGui/QMessageBox> +#include <QtGui/QShortcut> + +QT_BEGIN_NAMESPACE + +namespace { + #define TR(x) QCoreApplication::translate("BookmarkManager", x) +} + +BookmarkManagerWidget::BookmarkManagerWidget(BookmarkModel *sourceModel, + QWidget *parent) + : QWidget(parent) + , bookmarkModel(sourceModel) +{ + TRACE_OBJ + ui.setupUi(this); + + ui.treeView->setModel(bookmarkModel); + + ui.treeView->expandAll(); + ui.treeView->installEventFilter(this); + ui.treeView->viewport()->installEventFilter(this); + ui.treeView->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(ui.treeView, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(customContextMenuRequested(QPoint))); + + connect(ui.remove, SIGNAL(clicked()), this, SLOT(removeItem())); + connect(ui.lineEdit, SIGNAL(textChanged(QString)), this, + SLOT(textChanged(QString))); + new QShortcut(QKeySequence::Find, ui.lineEdit, SLOT(setFocus())); + + importExportMenu.addAction(tr("Import..."), this, SLOT(importBookmarks())); + importExportMenu.addAction(tr("Export..."), this, SLOT(exportBookmarks())); + ui.importExport->setMenu(&importExportMenu); + + new QShortcut(QKeySequence::FindNext, this, SLOT(findNext())); + new QShortcut(QKeySequence::FindPrevious, this, SLOT(findPrevious())); + + connect(bookmarkModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, + SLOT(refeshBookmarkCache())); + connect(bookmarkModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, + SLOT(refeshBookmarkCache())); + connect(bookmarkModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, + SLOT(refeshBookmarkCache())); + + ui.treeView->setCurrentIndex(ui.treeView->indexAt(QPoint(2, 2))); +} + +BookmarkManagerWidget::~BookmarkManagerWidget() +{ + TRACE_OBJ +} + +void BookmarkManagerWidget::closeEvent(QCloseEvent *event) +{ + TRACE_OBJ + event->accept(); + emit managerWidgetAboutToClose(); +} + +void BookmarkManagerWidget::renameItem(const QModelIndex &index) +{ + TRACE_OBJ + // check if we should rename the "Bookmarks Menu", bail + if (!bookmarkModel->parent(index).isValid()) + return; + + bookmarkModel->setItemsEditable(true); + ui.treeView->edit(index); + bookmarkModel->setItemsEditable(false); +} + +static int nextIndex(int current, int count, bool forward) +{ + TRACE_OBJ + if (current >= 0) + return (forward ? (current + 1) : ((current - 1) + count)) % count; + return 0; +} + +void BookmarkManagerWidget::selectNextIndex(bool direction) const +{ + QModelIndex current = ui.treeView->currentIndex(); + if (current.isValid() && !cache.isEmpty()) { + current = cache.at(nextIndex(cache.indexOf(current), cache.count(), + direction)); + } + ui.treeView->setCurrentIndex(current); +} + +bool BookmarkManagerWidget::eventFilter(QObject *object, QEvent *event) +{ + TRACE_OBJ + if (object != ui.treeView && object != ui.treeView->viewport()) + return QWidget::eventFilter(object, event); + + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + switch (ke->key()) { + case Qt::Key_F2: { + renameItem(ui.treeView->currentIndex()); + } break; + + case Qt::Key_Delete: { + removeItem(ui.treeView->currentIndex()); + } break; + + default: break; + } + } + + if (event->type() == QEvent::MouseButtonRelease) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + switch (me->button()) { + case Qt::LeftButton: { + if (me->modifiers() & Qt::ControlModifier) + setSourceFromIndex(ui.treeView->currentIndex(), true); + } break; + + case Qt::MidButton: { + setSourceFromIndex(ui.treeView->currentIndex(), true); + } break; + + default: break; + } + } + return QObject::eventFilter(object, event); +} + +void BookmarkManagerWidget::findNext() +{ + TRACE_OBJ + selectNextIndex(true); +} + +void BookmarkManagerWidget::findPrevious() +{ + TRACE_OBJ + selectNextIndex(false); +} + +void BookmarkManagerWidget::importBookmarks() +{ + TRACE_OBJ + const QString &fileName = QFileDialog::getOpenFileName(0, TR("Open File"), + QDir::currentPath(), TR("Files (*.xbel)")); + + if (fileName.isEmpty()) + return; + + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + XbelReader reader(bookmarkModel); + reader.readFromFile(&file); + } +} + +void BookmarkManagerWidget::exportBookmarks() +{ + TRACE_OBJ + QString fileName = QFileDialog::getSaveFileName(0, TR("Save File"), + QLatin1String("untitled.xbel"), TR("Files (*.xbel)")); + + const QLatin1String suffix(".xbel"); + if (!fileName.endsWith(suffix)) + fileName.append(suffix); + + QFile file(fileName); + if (file.open(QIODevice::WriteOnly)) { + XbelWriter writer(bookmarkModel); + writer.writeToFile(&file); + } else { + QMessageBox::information(this, TR("Qt Assistant"), + TR("Unable to save bookmarks."), TR("OK")); + } +} + +void BookmarkManagerWidget::refeshBookmarkCache() +{ + TRACE_OBJ + cache.clear(); + + const QString &text = ui.lineEdit->text(); + if (!text.isEmpty()) + cache = bookmarkModel->indexListFor(text); +} + +void BookmarkManagerWidget::textChanged(const QString &/*text*/) +{ + TRACE_OBJ + refeshBookmarkCache(); + if (!cache.isEmpty()) + ui.treeView->setCurrentIndex(cache.at(0)); +} + +void BookmarkManagerWidget::removeItem(const QModelIndex &index) +{ + TRACE_OBJ + QModelIndex current = index.isValid() ? index : ui.treeView->currentIndex(); + if (!bookmarkModel->parent(current).isValid()) + return; // check if we should delete the "Bookmarks Menu", bail + + if (bookmarkModel->hasChildren(current)) { + int value = QMessageBox::question(this, TR("Remove"), TR("You are going" + "to delete a Folder, this will also<br> remove it's content. Are " + "you sure to continue?"), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); + if (value == QMessageBox::Cancel) + return; + } + bookmarkModel->removeItem(current); +} + +void BookmarkManagerWidget::customContextMenuRequested(const QPoint &point) +{ + TRACE_OBJ + const QModelIndex &index = ui.treeView->indexAt(point); + if (!index.isValid()) + return; + + // check if we should open the menu on "Bookmarks Menu", bail + if (!bookmarkModel->parent(index).isValid()) + return; + + QAction *remove = 0; + QAction *rename = 0; + QAction *showItem = 0; + QAction *showItemInNewTab = 0; + + QMenu menu(QLatin1String("")); + if (bookmarkModel->data(index, UserRoleFolder).toBool()) { + remove = menu.addAction(TR("Delete Folder")); + rename = menu.addAction(TR("Rename Folder")); + } else { + showItem = menu.addAction(TR("Show Bookmark")); + showItemInNewTab = menu.addAction(TR("Show Bookmark in New Tab")); + menu.addSeparator(); + remove = menu.addAction(TR("Delete Bookmark")); + rename = menu.addAction(TR("Rename Bookmark")); + } + + QAction *pickedAction = menu.exec(ui.treeView->mapToGlobal(point)); + if (pickedAction == rename) + renameItem(index); + else if (pickedAction == remove) + removeItem(index); + else if (pickedAction == showItem || pickedAction == showItemInNewTab) + setSourceFromIndex(index, pickedAction == showItemInNewTab); +} + +void +BookmarkManagerWidget::setSourceFromIndex(const QModelIndex &index, bool newTab) +{ + TRACE_OBJ + if (bookmarkModel->data(index, UserRoleFolder).toBool()) + return; + + const QVariant &data = bookmarkModel->data(index, UserRoleUrl); + if (data.canConvert<QUrl>()) { + if (newTab) + emit setSourceInNewTab(data.toUrl()); + else + emit setSource(data.toUrl()); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/bookmarkmanagerwidget.h b/src/assistant/tools/assistant/bookmarkmanagerwidget.h new file mode 100644 index 000000000..a0dec72a6 --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkmanagerwidget.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef BOOKMARKMANAGERWIDGET_H +#define BOOKMARKMANAGERWIDGET_H + +#include "ui_bookmarkmanagerwidget.h" + +#include <QtCore/QPersistentModelIndex> + +#include <QtGui/QMenu> + +QT_BEGIN_NAMESPACE + +class BookmarkModel; +class QCloseEvent; +class QString; + +class BookmarkManagerWidget : public QWidget +{ + Q_OBJECT +public: + explicit BookmarkManagerWidget(BookmarkModel *bookmarkModel, + QWidget *parent = 0); + ~BookmarkManagerWidget(); + +protected: + void closeEvent(QCloseEvent *event); + +signals: + void setSource(const QUrl &url); + void setSourceInNewTab(const QUrl &url); + + void managerWidgetAboutToClose(); + +private: + void renameItem(const QModelIndex &index); + void selectNextIndex(bool direction) const; + bool eventFilter(QObject *object, QEvent *event); + +private slots: + void findNext(); + void findPrevious(); + + void importBookmarks(); + void exportBookmarks(); + + void refeshBookmarkCache(); + void textChanged(const QString &text); + + void removeItem(const QModelIndex &index = QModelIndex()); + + void customContextMenuRequested(const QPoint &point); + void setSourceFromIndex(const QModelIndex &index, bool newTab = false); + +private: + QMenu importExportMenu; + Ui::BookmarkManagerWidget ui; + QList<QPersistentModelIndex> cache; + + BookmarkModel *bookmarkModel; +}; + +QT_END_NAMESPACE + +#endif // BOOKMARKMANAGERWIDGET_H diff --git a/src/assistant/tools/assistant/bookmarkmanagerwidget.ui b/src/assistant/tools/assistant/bookmarkmanagerwidget.ui new file mode 100644 index 000000000..dc965d94e --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkmanagerwidget.ui @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BookmarkManagerWidget</class> + <widget class="QWidget" name="BookmarkManagerWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>517</width> + <height>348</height> + </rect> + </property> + <property name="windowTitle"> + <string>Manage Bookmarks</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Search:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="lineEdit"/> + </item> + </layout> + </item> + <item> + <widget class="QTreeView" name="treeView"> + <property name="acceptDrops"> + <bool>true</bool> + </property> + <property name="showDropIndicator" stdset="0"> + <bool>true</bool> + </property> + <property name="dragEnabled"> + <bool>true</bool> + </property> + <property name="autoExpandDelay"> + <number>1000</number> + </property> + <property name="uniformRowHeights"> + <bool>true</bool> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <attribute name="headerDefaultSectionSize"> + <number>225</number> + </attribute> + <attribute name="headerMinimumSectionSize"> + <number>50</number> + </attribute> + <attribute name="headerDefaultSectionSize"> + <number>225</number> + </attribute> + <attribute name="headerMinimumSectionSize"> + <number>50</number> + </attribute> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QPushButton" name="remove"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="importExport"> + <property name="text"> + <string>Import and Backup</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="pushButton_5"> + <property name="text"> + <string>OK</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton_5</sender> + <signal>clicked()</signal> + <receiver>BookmarkManagerWidget</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel"> + <x>445</x> + <y>328</y> + </hint> + <hint type="destinationlabel"> + <x>340</x> + <y>313</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/assistant/tools/assistant/bookmarkmodel.cpp b/src/assistant/tools/assistant/bookmarkmodel.cpp new file mode 100644 index 000000000..49b89c38f --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkmodel.cpp @@ -0,0 +1,461 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "bookmarkmodel.h" +#include "bookmarkitem.h" + +#include <QtCore/QMimeData> +#include <QtCore/QStack> + +#include <QtGui/QApplication> +#include <QtGui/QStyle> +#include <QtGui/QTreeView> + +const quint32 VERSION = 0xe53798; +const QLatin1String MIMETYPE("application/bookmarks.assistant"); + +BookmarkModel::BookmarkModel() + : QAbstractItemModel() + , m_folder(false) + , m_editable(false) + , rootItem(0) +{ +} + +BookmarkModel::~BookmarkModel() +{ + delete rootItem; +} + +QByteArray +BookmarkModel::bookmarks() const +{ + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + stream << qint32(VERSION); + + const QModelIndex &root = index(0,0, QModelIndex()).parent(); + for (int i = 0; i < rowCount(root); ++i) + collectItems(index(i, 0, root), 0, &stream); + + return ba; +} + +void +BookmarkModel::setBookmarks(const QByteArray &bookmarks) +{ + beginResetModel(); + + delete rootItem; + folderIcon = QApplication::style()->standardIcon(QStyle::SP_DirClosedIcon); + bookmarkIcon = QIcon(QLatin1String(":/trolltech/assistant/images/bookmark.png")); + + rootItem = new BookmarkItem(DataVector() << tr("Name") << tr("Address") + << true); + + QStack<BookmarkItem*> parents; + QDataStream stream(bookmarks); + + qint32 version; + stream >> version; + if (version < VERSION) { + stream.device()->seek(0); + BookmarkItem* toolbar = new BookmarkItem(DataVector() << tr("Toolbar Menu") + << QLatin1String("Folder") << true); + rootItem->addChild(toolbar); + + BookmarkItem* menu = new BookmarkItem(DataVector() << tr("Bookmarks Menu") + << QLatin1String("Folder") << true); + rootItem->addChild(menu); + parents.push(menu); + } else { + parents.push(rootItem); + } + + qint32 depth; + bool expanded; + QString name, url; + while (!stream.atEnd()) { + stream >> depth >> name >> url >> expanded; + while ((parents.count() - 1) != depth) + parents.pop(); + + BookmarkItem *item = new BookmarkItem(DataVector() << name << url << expanded); + if (url == QLatin1String("Folder")) { + parents.top()->addChild(item); + parents.push(item); + } else { + parents.top()->addChild(item); + } + } + + cache.clear(); + setupCache(index(0,0, QModelIndex().parent())); + endResetModel(); +} + +void +BookmarkModel::setItemsEditable(bool editable) +{ + m_editable = editable; +} + +void +BookmarkModel::expandFoldersIfNeeeded(QTreeView *treeView) +{ + foreach (const QModelIndex &index, cache) + treeView->setExpanded(index, index.data(UserRoleExpanded).toBool()); +} + +QModelIndex +BookmarkModel::addItem(const QModelIndex &parent, bool isFolder) +{ + m_folder = isFolder; + QModelIndex next; + if (insertRow(rowCount(parent), parent)) + next = index(rowCount(parent) - 1, 0, parent); + m_folder = false; + + return next; +} + +bool +BookmarkModel::removeItem(const QModelIndex &index) +{ + if (!index.isValid()) + return false; + + QModelIndexList indexes; + if (rowCount(index) > 0) + indexes = collectItems(index); + indexes.append(index); + + foreach (const QModelIndex &itemToRemove, indexes) { + if (!removeRow(itemToRemove.row(), itemToRemove.parent())) + return false; + cache.remove(itemFromIndex(itemToRemove)); + } + return true; +} + +int +BookmarkModel::rowCount(const QModelIndex &index) const +{ + if (BookmarkItem *item = itemFromIndex(index)) + return item->childCount(); + return 0; +} + +int +BookmarkModel::columnCount(const QModelIndex &/*index*/) const +{ + return 2; +} + +QModelIndex +BookmarkModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + if (BookmarkItem *childItem = itemFromIndex(index)) { + if (BookmarkItem *parent = childItem->parent()) { + if (parent != rootItem) + return createIndex(parent->childNumber(), 0, parent); + } + } + return QModelIndex(); +} + +QModelIndex +BookmarkModel::index(int row, int column, const QModelIndex &index) const +{ + if (index.isValid() && (index.column() != 0 && index.column() != 1)) + return QModelIndex(); + + if (BookmarkItem *parent = itemFromIndex(index)) { + if (BookmarkItem *childItem = parent->child(row)) + return createIndex(row, column, childItem); + } + return QModelIndex(); +} + +Qt::DropActions +BookmarkModel::supportedDropActions () const +{ + return /* Qt::CopyAction | */Qt::MoveAction; +} + +Qt::ItemFlags +BookmarkModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + Qt::ItemFlags defaultFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + if (m_editable) + defaultFlags |= Qt::ItemIsEditable; + + if (itemFromIndex(index) && index.data(UserRoleFolder).toBool()) { + if (index.column() > 0) + return defaultFlags &~ Qt::ItemIsEditable; + return defaultFlags | Qt::ItemIsDropEnabled; + } + + return defaultFlags | Qt::ItemIsDragEnabled; +} + +QVariant +BookmarkModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid()) { + if (BookmarkItem *item = itemFromIndex(index)) { + switch (role) { + case Qt::EditRole: { + case Qt::DisplayRole: + if (index.data(UserRoleFolder).toBool() && index.column() == 1) + return QLatin1String(""); + return item->data(index.column()); + } break; + + case Qt::DecorationRole: { + if (index.column() == 0) + return index.data(UserRoleFolder).toBool() + ? folderIcon : bookmarkIcon; + } break; + + default:; + return item->data(role); + } + } + } + return QVariant(); +} + +void BookmarkModel::setData(const QModelIndex &index, const DataVector &data) +{ + if (BookmarkItem *item = itemFromIndex(index)) { + item->setData(data); + emit dataChanged(index, index); + } +} + +bool +BookmarkModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + bool result = false; + if (role != Qt::EditRole && role != UserRoleExpanded) + return result; + + if (BookmarkItem *item = itemFromIndex(index)) { + if (role == Qt::EditRole) { + const bool isFolder = index.data(UserRoleFolder).toBool(); + if (!isFolder || (isFolder && index.column() == 0)) + result = item->setData(index.column(), value); + } else if (role == UserRoleExpanded) { + result = item->setData(UserRoleExpanded, value); + } + } + + if (result) + emit dataChanged(index, index); + return result; +} + +QVariant +BookmarkModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + if (rootItem && orientation == Qt::Horizontal && role == Qt::DisplayRole) + return rootItem->data(section); + return QVariant(); +} + +QModelIndex +BookmarkModel::indexFromItem(BookmarkItem *item) const +{ + return cache.value(item, QModelIndex()); +} + +BookmarkItem* +BookmarkModel::itemFromIndex(const QModelIndex &index) const +{ + if (index.isValid()) + return static_cast<BookmarkItem*>(index.internalPointer()); + return rootItem; +} + +QList<QPersistentModelIndex> +BookmarkModel::indexListFor(const QString &label) const +{ + QList<QPersistentModelIndex> hits; + const QModelIndexList &list = collectItems(QModelIndex()); + foreach(const QModelIndex &index, list) { + if (index.data().toString().contains(label, Qt::CaseInsensitive)) + hits.prepend(index); // list is reverse sorted + } + return hits; +} + +bool +BookmarkModel::insertRows(int position, int rows, const QModelIndex &parent) +{ + if (!parent.data(UserRoleFolder).toBool()) + return false; + + bool success = false; + if (BookmarkItem *parentItem = itemFromIndex(parent)) { + beginInsertRows(parent, position, position + rows - 1); + success = parentItem->insertChildren(m_folder, position, rows); + if (success) { + const QModelIndex ¤t = index(position, 0, parent); + cache.insert(itemFromIndex(current), current); + } + endInsertRows(); + } + return success; +} + +bool +BookmarkModel::removeRows(int position, int rows, const QModelIndex &index) +{ + bool success = false; + if (BookmarkItem *parent = itemFromIndex(index)) { + beginRemoveRows(index, position, position + rows - 1); + success = parent->removeChildren(position, rows); + endRemoveRows(); + } + return success; +} + +QStringList +BookmarkModel::mimeTypes() const +{ + return QStringList() << MIMETYPE; +} + +QMimeData* +BookmarkModel::mimeData(const QModelIndexList &indexes) const +{ + if (indexes.isEmpty()) + return 0; + + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + foreach (const QModelIndex &index, indexes) { + if (index.column() == 0) + collectItems(index, 0, &stream); + } + + QMimeData *mimeData = new QMimeData(); + mimeData->setData(MIMETYPE, data); + return mimeData; +} + +bool +BookmarkModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (!data->hasFormat(MIMETYPE) || column > 0) + return false; + + QByteArray ba = data->data(MIMETYPE); + QDataStream stream(&ba, QIODevice::ReadOnly); + while (stream.atEnd()) + return false; + + qint32 depth; + bool expanded; + QString name, url; + while (!stream.atEnd()) { + stream >> depth >> name >> url >> expanded; + if (insertRow(qMax(0, row), parent)) { + const QModelIndex ¤t = index(qMax(0, row), 0, parent); + if (current.isValid()) { + BookmarkItem* item = itemFromIndex(current); + item->setData(DataVector() << name << url << expanded); + } + } + } + return true; +} + +void +BookmarkModel::setupCache(const QModelIndex &parent) +{ + const QModelIndexList &list = collectItems(parent); + foreach (const QModelIndex &index, list) + cache.insert(itemFromIndex(index), index); +} + +QModelIndexList +BookmarkModel::collectItems(const QModelIndex &parent) const +{ + QModelIndexList list; + for (int i = rowCount(parent) - 1; i >= 0 ; --i) { + const QModelIndex &next = index(i, 0, parent); + if (data(next, UserRoleFolder).toBool()) + list += collectItems(next); + list.append(next); + } + return list; +} + +void +BookmarkModel::collectItems(const QModelIndex &parent, qint32 depth, + QDataStream *stream) const +{ + if (parent.isValid()) { + *stream << depth; + *stream << parent.data().toString(); + *stream << parent.data(UserRoleUrl).toString(); + *stream << parent.data(UserRoleExpanded).toBool(); + + for (int i = 0; i < rowCount(parent); ++i) { + if (parent.data(UserRoleFolder).toBool()) + collectItems(index(i, 0 , parent), depth + 1, stream); + } + } +} diff --git a/src/assistant/tools/assistant/bookmarkmodel.h b/src/assistant/tools/assistant/bookmarkmodel.h new file mode 100644 index 000000000..33c9e981d --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkmodel.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef BOOKMARKMODEL_H +#define BOOKMARKMODEL_H + +#include <QtCore/QAbstractItemModel> + +#include <QtGui/QIcon> + +QT_BEGIN_NAMESPACE + +class BookmarkItem; +class QMimeData; +class QTreeView; + +typedef QMap<BookmarkItem*, QPersistentModelIndex> ItemModelIndexCache; + +class BookmarkModel : public QAbstractItemModel +{ + Q_OBJECT +public: + BookmarkModel(); + ~BookmarkModel(); + + QByteArray bookmarks() const; + void setBookmarks(const QByteArray &bookmarks); + + void setItemsEditable(bool editable); + void expandFoldersIfNeeeded(QTreeView *treeView); + + QModelIndex addItem(const QModelIndex &parent, bool isFolder = false); + bool removeItem(const QModelIndex &index); + + int rowCount(const QModelIndex &index = QModelIndex()) const; + int columnCount(const QModelIndex &index = QModelIndex()) const; + + QModelIndex parent(const QModelIndex &index) const; + QModelIndex index(int row, int column, const QModelIndex &index) const; + + Qt::DropActions supportedDropActions () const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + QVariant data(const QModelIndex &index, int role) const; + void setData(const QModelIndex &index, const QVector<QVariant> &data); + bool setData(const QModelIndex &index, const QVariant &value, int role); + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + + QModelIndex indexFromItem(BookmarkItem *item) const; + BookmarkItem *itemFromIndex(const QModelIndex &index) const; + QList<QPersistentModelIndex> indexListFor(const QString &label) const; + + bool insertRows(int position, int rows, const QModelIndex &parent); + bool removeRows(int position, int rows, const QModelIndex &parent); + + QStringList mimeTypes() const; + QMimeData* mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, + int column, const QModelIndex &parent); + +private: + void setupCache(const QModelIndex &parent); + QModelIndexList collectItems(const QModelIndex &parent) const; + void collectItems(const QModelIndex &parent, qint32 depth, + QDataStream *stream) const; + +private: + int columns; + bool m_folder; + bool m_editable; + QIcon folderIcon; + QIcon bookmarkIcon; + QTreeView *treeView; + BookmarkItem *rootItem; + ItemModelIndexCache cache; +}; + +QT_END_NAMESPACE + +#endif // BOOKMARKMODEL_H diff --git a/src/assistant/tools/assistant/bookmarkwidget.ui b/src/assistant/tools/assistant/bookmarkwidget.ui new file mode 100644 index 000000000..a31a2779c --- /dev/null +++ b/src/assistant/tools/assistant/bookmarkwidget.ui @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BookmarkWidget</class> + <widget class="QWidget" name="BookmarkWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>235</width> + <height>606</height> + </rect> + </property> + <property name="windowTitle"> + <string>Bookmarks</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="margin"> + <number>4</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Filter:</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QLineEdit" name="lineEdit"/> + </item> + <item> + <widget class="QStackedWidget" name="stackedWidget"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="add"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="remove"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/assistant/tools/assistant/centralwidget.cpp b/src/assistant/tools/assistant/centralwidget.cpp new file mode 100644 index 000000000..c8c454f60 --- /dev/null +++ b/src/assistant/tools/assistant/centralwidget.cpp @@ -0,0 +1,636 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "centralwidget.h" + +#include "findwidget.h" +#include "helpenginewrapper.h" +#include "helpviewer.h" +#include "openpagesmanager.h" +#include "tracer.h" +#include "../shared/collectionconfiguration.h" + +#include <QtCore/QTimer> + +#include <QtGui/QKeyEvent> +#include <QtGui/QMenu> +#include <QtGui/QPageSetupDialog> +#include <QtGui/QPrintDialog> +#include <QtGui/QPrintPreviewDialog> +#include <QtGui/QPrinter> +#include <QtGui/QStackedWidget> +#include <QtGui/QTextBrowser> +#include <QtGui/QVBoxLayout> + +#include <QtHelp/QHelpSearchEngine> + +QT_BEGIN_NAMESPACE + +namespace { + CentralWidget *staticCentralWidget = 0; +} + +// -- TabBar + +TabBar::TabBar(QWidget *parent) + : QTabBar(parent) +{ + TRACE_OBJ +#ifdef Q_OS_MAC + setDocumentMode(true); +#endif + setMovable(true); + setShape(QTabBar::RoundedNorth); + setContextMenuPolicy(Qt::CustomContextMenu); + setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred, + QSizePolicy::TabWidget)); + connect(this, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int))); + connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(slotTabCloseRequested(int))); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(slotCustomContextMenuRequested(QPoint))); +} + +TabBar::~TabBar() +{ + TRACE_OBJ +} + +int TabBar::addNewTab(const QString &title) +{ + TRACE_OBJ + const int index = addTab(title); + setTabsClosable(count() > 1); + return index; +} + +void TabBar::setCurrent(HelpViewer *viewer) +{ + TRACE_OBJ + for (int i = 0; i < count(); ++i) { + HelpViewer *data = tabData(i).value<HelpViewer*>(); + if (data == viewer) { + setCurrentIndex(i); + break; + } + } +} + +void TabBar::removeTabAt(HelpViewer *viewer) +{ + TRACE_OBJ + for (int i = 0; i < count(); ++i) { + HelpViewer *data = tabData(i).value<HelpViewer*>(); + if (data == viewer) { + removeTab(i); + break; + } + } + setTabsClosable(count() > 1); +} + +void TabBar::titleChanged() +{ + TRACE_OBJ + for (int i = 0; i < count(); ++i) { + HelpViewer *data = tabData(i).value<HelpViewer*>(); + QString title = data->title(); + title.replace(QLatin1Char('&'), QLatin1String("&&")); + setTabText(i, title.isEmpty() ? tr("(Untitled)") : title); + } +} + +void TabBar::slotCurrentChanged(int index) +{ + TRACE_OBJ + emit currentTabChanged(tabData(index).value<HelpViewer*>()); +} + +void TabBar::slotTabCloseRequested(int index) +{ + TRACE_OBJ + OpenPagesManager::instance()->closePage(tabData(index).value<HelpViewer*>()); +} + +void TabBar::slotCustomContextMenuRequested(const QPoint &pos) +{ + TRACE_OBJ + const int tab = tabAt(pos); + if (tab < 0) + return; + + QMenu menu(QLatin1String(""), this); + menu.addAction(tr("New &Tab"), OpenPagesManager::instance(), SLOT(createPage())); + + const bool enableAction = count() > 1; + QAction *closePage = menu.addAction(tr("&Close Tab")); + closePage->setEnabled(enableAction); + + QAction *closePages = menu.addAction(tr("Close Other Tabs")); + closePages->setEnabled(enableAction); + + menu.addSeparator(); + + HelpViewer *viewer = tabData(tab).value<HelpViewer*>(); + QAction *newBookmark = menu.addAction(tr("Add Bookmark for this Page...")); + const QString &url = viewer->source().toString(); + if (url.isEmpty() || url == QLatin1String("about:blank")) + newBookmark->setEnabled(false); + + QAction *pickedAction = menu.exec(mapToGlobal(pos)); + if (pickedAction == closePage) + slotTabCloseRequested(tab); + else if (pickedAction == closePages) { + for (int i = count() - 1; i >= 0; --i) { + if (i != tab) + slotTabCloseRequested(i); + } + } else if (pickedAction == newBookmark) + emit addBookmark(viewer->title(), url); +} + +// -- CentralWidget + +CentralWidget::CentralWidget(QWidget *parent) + : QWidget(parent) +#ifndef QT_NO_PRINTER + , m_printer(0) +#endif + , m_findWidget(new FindWidget(this)) + , m_stackedWidget(new QStackedWidget(this)) + , m_tabBar(new TabBar(this)) +{ + TRACE_OBJ + staticCentralWidget = this; + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + vboxLayout->setMargin(0); + vboxLayout->setSpacing(0); + vboxLayout->addWidget(m_tabBar); + m_tabBar->setVisible(HelpEngineWrapper::instance().showTabs()); + vboxLayout->addWidget(m_stackedWidget); + vboxLayout->addWidget(m_findWidget); + m_findWidget->hide(); + + connect(m_findWidget, SIGNAL(findNext()), this, SLOT(findNext())); + connect(m_findWidget, SIGNAL(findPrevious()), this, SLOT(findPrevious())); + connect(m_findWidget, SIGNAL(find(QString, bool, bool)), this, + SLOT(find(QString, bool, bool))); + connect(m_findWidget, SIGNAL(escapePressed()), this, SLOT(activateTab())); + connect(m_tabBar, SIGNAL(addBookmark(QString, QString)), this, + SIGNAL(addBookmark(QString, QString))); +} + +CentralWidget::~CentralWidget() +{ + TRACE_OBJ + QStringList zoomFactors; + QStringList currentPages; + for (int i = 0; i < m_stackedWidget->count(); ++i) { + const HelpViewer * const viewer = viewerAt(i); + const QUrl &source = viewer->source(); + if (source.isValid()) { + currentPages << source.toString(); + zoomFactors << QString::number(viewer->scale()); + } + } + + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + helpEngine.setLastShownPages(currentPages); + helpEngine.setLastZoomFactors(zoomFactors); + helpEngine.setLastTabPage(m_stackedWidget->currentIndex()); + +#ifndef QT_NO_PRINTER + delete m_printer; +#endif +} + +CentralWidget *CentralWidget::instance() +{ + TRACE_OBJ + return staticCentralWidget; +} + +QUrl CentralWidget::currentSource() const +{ + TRACE_OBJ + return currentHelpViewer()->source(); +} + +QString CentralWidget::currentTitle() const +{ + TRACE_OBJ + return currentHelpViewer()->title(); +} + +bool CentralWidget::hasSelection() const +{ + TRACE_OBJ + return !currentHelpViewer()->selectedText().isEmpty(); +} + +bool CentralWidget::isForwardAvailable() const +{ + TRACE_OBJ + return currentHelpViewer()->isForwardAvailable(); +} + +bool CentralWidget::isBackwardAvailable() const +{ + TRACE_OBJ + return currentHelpViewer()->isBackwardAvailable(); +} + +HelpViewer* CentralWidget::viewerAt(int index) const +{ + TRACE_OBJ + return static_cast<HelpViewer*>(m_stackedWidget->widget(index)); +} + +HelpViewer* CentralWidget::currentHelpViewer() const +{ + TRACE_OBJ + return static_cast<HelpViewer *>(m_stackedWidget->currentWidget()); +} + +void CentralWidget::addPage(HelpViewer *page, bool fromSearch) +{ + TRACE_OBJ + page->installEventFilter(this); + page->setFocus(Qt::OtherFocusReason); + connectSignals(page); + const int index = m_stackedWidget->addWidget(page); + m_tabBar->setTabData(m_tabBar->addNewTab(page->title()), + QVariant::fromValue(viewerAt(index))); + connect (page, SIGNAL(titleChanged()), m_tabBar, SLOT(titleChanged())); + + if (fromSearch) { + connect(currentHelpViewer(), SIGNAL(loadFinished(bool)), this, + SLOT(highlightSearchTerms())); + } +} + +void CentralWidget::removePage(int index) +{ + TRACE_OBJ + const bool currentChanged = index == currentIndex(); + m_tabBar->removeTabAt(viewerAt(index)); + m_stackedWidget->removeWidget(m_stackedWidget->widget(index)); + if (currentChanged) + emit currentViewerChanged(); +} + +int CentralWidget::currentIndex() const +{ + TRACE_OBJ + return m_stackedWidget->currentIndex(); +} + +void CentralWidget::setCurrentPage(HelpViewer *page) +{ + TRACE_OBJ + m_tabBar->setCurrent(page); + m_stackedWidget->setCurrentWidget(page); + emit currentViewerChanged(); +} + +void CentralWidget::connectTabBar() +{ + TRACE_OBJ + connect(m_tabBar, SIGNAL(currentTabChanged(HelpViewer*)), + OpenPagesManager::instance(), SLOT(setCurrentPage(HelpViewer*))); +} + +// -- public slots + +void CentralWidget::copy() +{ + TRACE_OBJ + currentHelpViewer()->copy(); +} + +void CentralWidget::home() +{ + TRACE_OBJ + currentHelpViewer()->home(); +} + +void CentralWidget::zoomIn() +{ + TRACE_OBJ + currentHelpViewer()->scaleUp(); +} + +void CentralWidget::zoomOut() +{ + TRACE_OBJ + currentHelpViewer()->scaleDown(); +} + +void CentralWidget::resetZoom() +{ + TRACE_OBJ + currentHelpViewer()->resetScale(); +} + +void CentralWidget::forward() +{ + TRACE_OBJ + currentHelpViewer()->forward(); +} + +void CentralWidget::nextPage() +{ + TRACE_OBJ + m_stackedWidget->setCurrentIndex((m_stackedWidget->currentIndex() + 1) + % m_stackedWidget->count()); +} + +void CentralWidget::backward() +{ + TRACE_OBJ + currentHelpViewer()->backward(); +} + +void CentralWidget::previousPage() +{ + TRACE_OBJ + m_stackedWidget->setCurrentIndex((m_stackedWidget->currentIndex() - 1) + % m_stackedWidget->count()); +} + +void CentralWidget::print() +{ + TRACE_OBJ +#ifndef QT_NO_PRINTER + initPrinter(); + QPrintDialog dlg(m_printer, this); + + if (!currentHelpViewer()->selectedText().isEmpty()) + dlg.addEnabledOption(QAbstractPrintDialog::PrintSelection); + dlg.addEnabledOption(QAbstractPrintDialog::PrintPageRange); + dlg.addEnabledOption(QAbstractPrintDialog::PrintCollateCopies); + dlg.setWindowTitle(tr("Print Document")); + if (dlg.exec() == QDialog::Accepted) + currentHelpViewer()->print(m_printer); +#endif +} + +void CentralWidget::pageSetup() +{ + TRACE_OBJ +#ifndef QT_NO_PRINTER + initPrinter(); + QPageSetupDialog dlg(m_printer); + dlg.exec(); +#endif +} + +void CentralWidget::printPreview() +{ + TRACE_OBJ +#ifndef QT_NO_PRINTER + initPrinter(); + QPrintPreviewDialog preview(m_printer, this); + connect(&preview, SIGNAL(paintRequested(QPrinter*)), + SLOT(printPreview(QPrinter*))); + preview.exec(); +#endif +} + +void CentralWidget::setSource(const QUrl &url) +{ + TRACE_OBJ + HelpViewer *viewer = currentHelpViewer(); + viewer->setSource(url); + viewer->setFocus(Qt::OtherFocusReason); +} + +void CentralWidget::setSourceFromSearch(const QUrl &url) +{ + TRACE_OBJ + connect(currentHelpViewer(), SIGNAL(loadFinished(bool)), this, + SLOT(highlightSearchTerms())); + currentHelpViewer()->setSource(url); + currentHelpViewer()->setFocus(Qt::OtherFocusReason); +} + +void CentralWidget::findNext() +{ + TRACE_OBJ + find(m_findWidget->text(), true, false); +} + +void CentralWidget::findPrevious() +{ + TRACE_OBJ + find(m_findWidget->text(), false, false); +} + +void CentralWidget::find(const QString &ttf, bool forward, bool incremental) +{ + TRACE_OBJ + bool found = false; + if (HelpViewer *viewer = currentHelpViewer()) { + HelpViewer::FindFlags flags = 0; + if (!forward) + flags |= HelpViewer::FindBackward; + if (m_findWidget->caseSensitive()) + flags |= HelpViewer::FindCaseSensitively; + found = viewer->findText(ttf, flags, incremental, false); + } + + if (!found && ttf.isEmpty()) + found = true; // the line edit is empty, no need to mark it red... + + if (!m_findWidget->isVisible()) + m_findWidget->show(); + m_findWidget->setPalette(found); +} + +void CentralWidget::activateTab() +{ + TRACE_OBJ + currentHelpViewer()->setFocus(); +} + +void CentralWidget::showTextSearch() +{ + TRACE_OBJ + m_findWidget->show(); +} + +void CentralWidget::updateBrowserFont() +{ + TRACE_OBJ + const int count = m_stackedWidget->count(); + const QFont &font = viewerAt(count - 1)->viewerFont(); + for (int i = 0; i < count; ++i) + viewerAt(i)->setViewerFont(font); +} + +void CentralWidget::updateUserInterface() +{ + m_tabBar->setVisible(HelpEngineWrapper::instance().showTabs()); +} + +// -- protected + +void CentralWidget::keyPressEvent(QKeyEvent *e) +{ + TRACE_OBJ + const QString &text = e->text(); + if (text.startsWith(QLatin1Char('/'))) { + if (!m_findWidget->isVisible()) { + m_findWidget->showAndClear(); + } else { + m_findWidget->show(); + } + } else { + QWidget::keyPressEvent(e); + } +} + +void CentralWidget::focusInEvent(QFocusEvent * /* event */) +{ + TRACE_OBJ + // If we have a current help viewer then this is the 'focus proxy', + // otherwise it's the central widget. This is needed, so an embedding + // program can just set the focus to the central widget and it does + // The Right Thing(TM) + QObject *receiver = m_stackedWidget; + if (HelpViewer *viewer = currentHelpViewer()) + receiver = viewer; + QTimer::singleShot(1, receiver, SLOT(setFocus())); +} + +// -- private slots + +void CentralWidget::highlightSearchTerms() +{ + TRACE_OBJ + QHelpSearchEngine *searchEngine = + HelpEngineWrapper::instance().searchEngine(); + QList<QHelpSearchQuery> queryList = searchEngine->query(); + + QStringList terms; + foreach (const QHelpSearchQuery &query, queryList) { + switch (query.fieldName) { + default: break; + case QHelpSearchQuery::ALL: { + case QHelpSearchQuery::PHRASE: + case QHelpSearchQuery::DEFAULT: + case QHelpSearchQuery::ATLEAST: + foreach (QString term, query.wordList) + terms.append(term.remove(QLatin1Char('"'))); + } + } + } + + HelpViewer *viewer = currentHelpViewer(); + foreach (const QString& term, terms) + viewer->findText(term, 0, false, true); + disconnect(viewer, SIGNAL(loadFinished(bool)), this, + SLOT(highlightSearchTerms())); +} + +void CentralWidget::printPreview(QPrinter *p) +{ + TRACE_OBJ +#ifndef QT_NO_PRINTER + currentHelpViewer()->print(p); +#endif +} + +void CentralWidget::handleSourceChanged(const QUrl &url) +{ + TRACE_OBJ + if (sender() == currentHelpViewer()) + emit sourceChanged(url); +} + +// -- private + +void CentralWidget::initPrinter() +{ + TRACE_OBJ +#ifndef QT_NO_PRINTER + if (!m_printer) + m_printer = new QPrinter(QPrinter::HighResolution); +#endif +} + +void CentralWidget::connectSignals(HelpViewer *page) +{ + TRACE_OBJ + connect(page, SIGNAL(copyAvailable(bool)), this, + SIGNAL(copyAvailable(bool))); + connect(page, SIGNAL(forwardAvailable(bool)), this, + SIGNAL(forwardAvailable(bool))); + connect(page, SIGNAL(backwardAvailable(bool)), this, + SIGNAL(backwardAvailable(bool))); + connect(page, SIGNAL(sourceChanged(QUrl)), this, + SLOT(handleSourceChanged(QUrl))); + connect(page, SIGNAL(highlighted(QString)), this, + SIGNAL(highlighted(QString))); + connect(page, SIGNAL(printRequested()), this, SLOT(print())); +} + +bool CentralWidget::eventFilter(QObject *object, QEvent *e) +{ + TRACE_OBJ + if (e->type() != QEvent::KeyPress) + return QWidget::eventFilter(object, e); + + HelpViewer *viewer = currentHelpViewer(); + QKeyEvent *keyEvent = static_cast<QKeyEvent*> (e); + if (viewer == object && keyEvent->key() == Qt::Key_Backspace) { + if (viewer->isBackwardAvailable()) { +#if !defined(QT_NO_WEBKIT) + // this helps in case there is an html <input> field + if (!viewer->hasFocus()) +#endif + viewer->backward(); + } + } + return QWidget::eventFilter(object, e); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/centralwidget.h b/src/assistant/tools/assistant/centralwidget.h new file mode 100644 index 000000000..2645fa8df --- /dev/null +++ b/src/assistant/tools/assistant/centralwidget.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CENTRALWIDGET_H +#define CENTRALWIDGET_H + +#include <QtCore/QUrl> + +#include <QtGui/QTabBar> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class FindWidget; +class HelpViewer; +class QStackedWidget; + +class TabBar : public QTabBar +{ + Q_OBJECT +public: + TabBar(QWidget *parent = 0); + ~TabBar(); + + int addNewTab(const QString &title); + void setCurrent(HelpViewer *viewer); + void removeTabAt(HelpViewer *viewer); + +public slots: + void titleChanged(); + +signals: + void currentTabChanged(HelpViewer *viewer); + void addBookmark(const QString &title, const QString &url); + +private slots: + void slotCurrentChanged(int index); + void slotTabCloseRequested(int index); + void slotCustomContextMenuRequested(const QPoint &pos); +}; + +class CentralWidget : public QWidget +{ + Q_OBJECT + +public: + CentralWidget(QWidget *parent = 0); + ~CentralWidget(); + + static CentralWidget *instance(); + + QUrl currentSource() const; + QString currentTitle() const; + + bool hasSelection() const; + bool isForwardAvailable() const; + bool isBackwardAvailable() const; + + HelpViewer *viewerAt(int index) const; + HelpViewer *currentHelpViewer() const; + + void addPage(HelpViewer *page, bool fromSearch = false); + void removePage(int index); + + int currentIndex() const; + void setCurrentPage(HelpViewer *page); + + void connectTabBar(); + +public slots: + void copy(); + void home(); + + void zoomIn(); + void zoomOut(); + void resetZoom(); + + void forward(); + void nextPage(); + + void backward(); + void previousPage(); + + void print(); + void pageSetup(); + void printPreview(); + + void setSource(const QUrl &url); + void setSourceFromSearch(const QUrl &url); + + void findNext(); + void findPrevious(); + void find(const QString &text, bool forward, bool incremental); + + void activateTab(); + void showTextSearch(); + void updateBrowserFont(); + void updateUserInterface(); + +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 addBookmark(const QString &title, const QString &url); + +protected: + void keyPressEvent(QKeyEvent *); + void focusInEvent(QFocusEvent *event); + +private slots: + void highlightSearchTerms(); + void printPreview(QPrinter *printer); + void handleSourceChanged(const QUrl &url); + +private: + void initPrinter(); + void connectSignals(HelpViewer *page); + bool eventFilter(QObject *object, QEvent *e); + +private: +#ifndef QT_NO_PRINTER + QPrinter *m_printer; +#endif + FindWidget *m_findWidget; + QStackedWidget *m_stackedWidget; + TabBar *m_tabBar; +}; + +QT_END_NAMESPACE + +#endif // CENTRALWIDGET_H diff --git a/src/assistant/tools/assistant/cmdlineparser.cpp b/src/assistant/tools/assistant/cmdlineparser.cpp new file mode 100644 index 000000000..6a239d305 --- /dev/null +++ b/src/assistant/tools/assistant/cmdlineparser.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include <QtCore/QFileInfo> +#include <QtCore/QStringBuilder> +#include <QtGui/QMessageBox> + +#include "cmdlineparser.h" + +QT_BEGIN_NAMESPACE + +static const char helpMessage[] = QT_TRANSLATE_NOOP("CmdLineParser", + "Usage: assistant [Options]\n\n" + "-collectionFile file Uses the specified collection\n" + " file instead of the default one\n" + "-showUrl url Shows the document with the\n" + " url.\n" + "-enableRemoteControl Enables Assistant to be\n" + " remotely controlled.\n" + "-show widget Shows the specified dockwidget\n" + " which can be \"contents\", \"index\",\n" + " \"bookmarks\" or \"search\".\n" + "-activate widget Activates the specified dockwidget\n" + " which can be \"contents\", \"index\",\n" + " \"bookmarks\" or \"search\".\n" + "-hide widget Hides the specified dockwidget\n" + " which can be \"contents\", \"index\"\n" + " \"bookmarks\" or \"search\".\n" + "-register helpFile Registers the specified help file\n" + " (.qch) in the given collection\n" + " file.\n" + "-unregister helpFile Unregisters the specified help file\n" + " (.qch) from the give collection\n" + " file.\n" + "-setCurrentFilter filter Set the filter as the active filter.\n" + "-remove-search-index Removes the full text search index.\n" + "-rebuild-search-index Re-builds the full text search index (potentially slow).\n" + "-quiet Does not display any error or\n" + " status message.\n" + "-help Displays this help.\n" + ); + + +CmdLineParser::CmdLineParser(const QStringList &arguments) + : m_pos(0), + m_enableRemoteControl(false), + m_contents(Untouched), + m_index(Untouched), + m_bookmarks(Untouched), + m_search(Untouched), + m_register(None), + m_removeSearchIndex(false), + m_rebuildSearchIndex(false), + m_quiet(false) +{ + TRACE_OBJ + for (int i = 1; i < arguments.count(); ++i) { + const QString &arg = arguments.at(i); + if (arg.toLower() == "-quiet") + m_quiet = true; + else + m_arguments.append(arg); + } +} + +CmdLineParser::Result CmdLineParser::parse() +{ + TRACE_OBJ + bool showHelp = false; + + while (m_error.isEmpty() && hasMoreArgs()) { + const QString &arg = nextArg().toLower(); + if (arg == QLatin1String("-collectionfile")) + handleCollectionFileOption(); + else if (arg == QLatin1String("-showurl")) + handleShowUrlOption(); + else if (arg == QLatin1String("-enableremotecontrol")) + m_enableRemoteControl = true; + else if (arg == QLatin1String("-show")) + handleShowOption(); + else if (arg == QLatin1String("-hide")) + handleHideOption(); + else if (arg == QLatin1String("-activate")) + handleActivateOption(); + else if (arg == QLatin1String("-register")) + handleRegisterOption(); + else if (arg == QLatin1String("-unregister")) + handleUnregisterOption(); + else if (arg == QLatin1String("-setcurrentfilter")) + handleSetCurrentFilterOption(); + else if (arg == QLatin1String("-remove-search-index")) + m_removeSearchIndex = true; + else if (arg == QLatin1String("-rebuild-search-index")) + m_rebuildSearchIndex = true; + else if (arg == QLatin1String("-help")) + showHelp = true; + else + m_error = tr("Unknown option: %1").arg(arg); + } + + if (!m_error.isEmpty()) { + showMessage(m_error + QLatin1String("\n\n\n") + tr(helpMessage), true); + return Error; + } else if (showHelp) { + showMessage(tr(helpMessage), false); + return Help; + } + return Ok; +} + +bool CmdLineParser::hasMoreArgs() const +{ + TRACE_OBJ + return m_pos < m_arguments.count(); +} + +const QString &CmdLineParser::nextArg() +{ + TRACE_OBJ + Q_ASSERT(hasMoreArgs()); + return m_arguments.at(m_pos++); +} + +void CmdLineParser::handleCollectionFileOption() +{ + TRACE_OBJ + if (hasMoreArgs()) { + const QString &fileName = nextArg(); + m_collectionFile = getFileName(fileName); + if (m_collectionFile.isEmpty()) + m_error = tr("The collection file '%1' does not exist."). + arg(fileName); + } else { + m_error = tr("Missing collection file."); + } +} + +void CmdLineParser::handleShowUrlOption() +{ + TRACE_OBJ + if (hasMoreArgs()) { + const QString &urlString = nextArg(); + QUrl url(urlString); + if (url.isValid()) { + m_url = url; + } else + m_error = tr("Invalid URL '%1'.").arg(urlString); + } else { + m_error = tr("Missing URL."); + } +} + +void CmdLineParser::handleShowOption() +{ + TRACE_OBJ + handleShowOrHideOrActivateOption(Show); +} + +void CmdLineParser::handleHideOption() +{ + TRACE_OBJ + handleShowOrHideOrActivateOption(Hide); +} + +void CmdLineParser::handleActivateOption() +{ + TRACE_OBJ + handleShowOrHideOrActivateOption(Activate); +} + +void CmdLineParser::handleShowOrHideOrActivateOption(ShowState state) +{ + TRACE_OBJ + if (hasMoreArgs()) { + const QString &widget = nextArg().toLower(); + if (widget == QLatin1String("contents")) + m_contents = state; + else if (widget == QLatin1String("index")) + m_index = state; + else if (widget == QLatin1String("bookmarks")) + m_bookmarks = state; + else if (widget == QLatin1String("search")) + m_search = state; + else + m_error = tr("Unknown widget: %1").arg(widget); + } else { + m_error = tr("Missing widget."); + } +} + +void CmdLineParser::handleRegisterOption() +{ + TRACE_OBJ + handleRegisterOrUnregisterOption(Register); +} + +void CmdLineParser::handleUnregisterOption() +{ + TRACE_OBJ + handleRegisterOrUnregisterOption(Unregister); +} + +void CmdLineParser::handleRegisterOrUnregisterOption(RegisterState state) +{ + TRACE_OBJ + if (hasMoreArgs()) { + const QString &fileName = nextArg(); + m_helpFile = getFileName(fileName); + if (m_helpFile.isEmpty()) + m_error = tr("The Qt help file '%1' does not exist.").arg(fileName); + else + m_register = state; + } else { + m_error = tr("Missing help file."); + } +} + +void CmdLineParser::handleSetCurrentFilterOption() +{ + TRACE_OBJ + if (hasMoreArgs()) + m_currentFilter = nextArg(); + else + m_error = tr("Missing filter argument."); +} + +QString CmdLineParser::getFileName(const QString &fileName) +{ + TRACE_OBJ + QFileInfo fi(fileName); + if (!fi.exists()) + return QString(); + return fi.absoluteFilePath(); +} + +void CmdLineParser::showMessage(const QString &msg, bool error) +{ + TRACE_OBJ + if (m_quiet) + return; +#ifdef Q_OS_WIN + QString message = QLatin1String("<pre>") % msg % QLatin1String("</pre>"); + if (error) + QMessageBox::critical(0, tr("Error"), message); + else + QMessageBox::information(0, tr("Notice"), message); +#else + fprintf(error ? stderr : stdout, "%s\n", qPrintable(msg)); +#endif +} + +void CmdLineParser::setCollectionFile(const QString &file) +{ + TRACE_OBJ + m_collectionFile = file; +} + +QString CmdLineParser::collectionFile() const +{ + TRACE_OBJ + return m_collectionFile; +} + +bool CmdLineParser::collectionFileGiven() const +{ + TRACE_OBJ + return m_arguments.contains(QLatin1String("-collectionfile"), + Qt::CaseInsensitive); +} + +QUrl CmdLineParser::url() const +{ + TRACE_OBJ + return m_url; +} + +bool CmdLineParser::enableRemoteControl() const +{ + TRACE_OBJ + return m_enableRemoteControl; +} + +CmdLineParser::ShowState CmdLineParser::contents() const +{ + TRACE_OBJ + return m_contents; +} + +CmdLineParser::ShowState CmdLineParser::index() const +{ + TRACE_OBJ + return m_index; +} + +CmdLineParser::ShowState CmdLineParser::bookmarks() const +{ + TRACE_OBJ + return m_bookmarks; +} + +CmdLineParser::ShowState CmdLineParser::search() const +{ + TRACE_OBJ + return m_search; +} + +QString CmdLineParser::currentFilter() const +{ + TRACE_OBJ + return m_currentFilter; +} + +bool CmdLineParser::removeSearchIndex() const +{ + TRACE_OBJ + return m_removeSearchIndex; +} + +bool CmdLineParser::rebuildSearchIndex() const +{ + TRACE_OBJ + return m_rebuildSearchIndex; +} + +CmdLineParser::RegisterState CmdLineParser::registerRequest() const +{ + TRACE_OBJ + return m_register; +} + +QString CmdLineParser::helpFile() const +{ + TRACE_OBJ + return m_helpFile; +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/cmdlineparser.h b/src/assistant/tools/assistant/cmdlineparser.h new file mode 100644 index 000000000..f45679f66 --- /dev/null +++ b/src/assistant/tools/assistant/cmdlineparser.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CMDLINEPARSER_H +#define CMDLINEPARSER_H + +#include <QtCore/QCoreApplication> +#include <QtCore/QStringList> +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +class CmdLineParser +{ + Q_DECLARE_TR_FUNCTIONS(CmdLineParser) +public: + enum Result {Ok, Help, Error}; + enum ShowState {Untouched, Show, Hide, Activate}; + enum RegisterState {None, Register, Unregister}; + + CmdLineParser(const QStringList &arguments); + Result parse(); + + void setCollectionFile(const QString &file); + QString collectionFile() const; + bool collectionFileGiven() const; + QString cloneFile() const; + QUrl url() const; + bool enableRemoteControl() const; + ShowState contents() const; + ShowState index() const; + ShowState bookmarks() const; + ShowState search() const; + QString currentFilter() const; + bool removeSearchIndex() const; + bool rebuildSearchIndex() const; + RegisterState registerRequest() const; + QString helpFile() const; + + void showMessage(const QString &msg, bool error); + +private: + QString getFileName(const QString &fileName); + bool hasMoreArgs() const; + const QString &nextArg(); + void handleCollectionFileOption(); + void handleShowUrlOption(); + void handleShowOption(); + void handleHideOption(); + void handleActivateOption(); + void handleShowOrHideOrActivateOption(ShowState state); + void handleRegisterOption(); + void handleUnregisterOption(); + void handleRegisterOrUnregisterOption(RegisterState state); + void handleSetCurrentFilterOption(); + + QStringList m_arguments; + int m_pos; + QString m_collectionFile; + QString m_cloneFile; + QString m_helpFile; + QUrl m_url; + bool m_enableRemoteControl; + + ShowState m_contents; + ShowState m_index; + ShowState m_bookmarks; + ShowState m_search; + RegisterState m_register; + QString m_currentFilter; + bool m_removeSearchIndex; + bool m_rebuildSearchIndex; + bool m_quiet; + QString m_error; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/assistant/tools/assistant/contentwindow.cpp b/src/assistant/tools/assistant/contentwindow.cpp new file mode 100644 index 000000000..fbf70aa1f --- /dev/null +++ b/src/assistant/tools/assistant/contentwindow.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "contentwindow.h" + +#include "centralwidget.h" +#include "helpenginewrapper.h" +#include "helpviewer.h" +#include "openpagesmanager.h" +#include "tracer.h" + +#include <QtGui/QLayout> +#include <QtGui/QFocusEvent> +#include <QtGui/QMenu> + +#include <QtHelp/QHelpContentWidget> + +QT_BEGIN_NAMESPACE + +ContentWindow::ContentWindow() + : m_contentWidget(HelpEngineWrapper::instance().contentWidget()) + , m_expandDepth(-2) +{ + TRACE_OBJ + m_contentWidget->viewport()->installEventFilter(this); + m_contentWidget->setContextMenuPolicy(Qt::CustomContextMenu); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(4); + layout->addWidget(m_contentWidget); + + connect(m_contentWidget, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(showContextMenu(QPoint))); + connect(m_contentWidget, SIGNAL(linkActivated(QUrl)), this, + SIGNAL(linkActivated(QUrl))); + + QHelpContentModel *contentModel = + qobject_cast<QHelpContentModel*>(m_contentWidget->model()); + connect(contentModel, SIGNAL(contentsCreated()), this, SLOT(expandTOC())); +} + +ContentWindow::~ContentWindow() +{ + TRACE_OBJ +} + +bool ContentWindow::syncToContent(const QUrl& url) +{ + TRACE_OBJ + QModelIndex idx = m_contentWidget->indexOf(url); + if (!idx.isValid()) + return false; + m_contentWidget->setCurrentIndex(idx); + return true; +} + +void ContentWindow::expandTOC() +{ + TRACE_OBJ + Q_ASSERT(m_expandDepth >= -2); + if (m_expandDepth > -2) { + expandToDepth(m_expandDepth); + m_expandDepth = -2; + } +} + +void ContentWindow::expandToDepth(int depth) +{ + TRACE_OBJ + Q_ASSERT(depth >= -2); + m_expandDepth = depth; + if (depth == -1) + m_contentWidget->expandAll(); + else if (depth == 0) + m_contentWidget->collapseAll(); + else + m_contentWidget->expandToDepth(depth - 1); +} + +void ContentWindow::focusInEvent(QFocusEvent *e) +{ + TRACE_OBJ + if (e->reason() != Qt::MouseFocusReason) + m_contentWidget->setFocus(); +} + +void ContentWindow::keyPressEvent(QKeyEvent *e) +{ + TRACE_OBJ + if (e->key() == Qt::Key_Escape) + emit escapePressed(); +} + +bool ContentWindow::eventFilter(QObject *o, QEvent *e) +{ + TRACE_OBJ + if (m_contentWidget && o == m_contentWidget->viewport() + && e->type() == QEvent::MouseButtonRelease) { + QMouseEvent *me = static_cast<QMouseEvent*>(e); + const QModelIndex &index = m_contentWidget->indexAt(me->pos()); + if (!index.isValid()) + return QWidget::eventFilter(o, e); + + const Qt::MouseButtons button = me->button(); + QItemSelectionModel *sm = m_contentWidget->selectionModel(); + if (sm->isSelected(index)) { + if ((button == Qt::LeftButton && (me->modifiers() & Qt::ControlModifier)) + || (button == Qt::MidButton)) { + QHelpContentModel *contentModel = + qobject_cast<QHelpContentModel*>(m_contentWidget->model()); + if (contentModel) { + QHelpContentItem *itm = contentModel->contentItemAt(index); + if (itm && HelpViewer::canOpenPage(itm->url().path())) + OpenPagesManager::instance()->createPage(itm->url()); + } + } else if (button == Qt::LeftButton) { + itemClicked(index); + } + } + } + return QWidget::eventFilter(o, e); +} + + +void ContentWindow::showContextMenu(const QPoint &pos) +{ + TRACE_OBJ + if (!m_contentWidget->indexAt(pos).isValid()) + return; + + QHelpContentModel *contentModel = + qobject_cast<QHelpContentModel*>(m_contentWidget->model()); + QHelpContentItem *itm = + contentModel->contentItemAt(m_contentWidget->currentIndex()); + + QMenu menu; + QAction *curTab = menu.addAction(tr("Open Link")); + QAction *newTab = menu.addAction(tr("Open Link in New Tab")); + if (!HelpViewer::canOpenPage(itm->url().path())) + newTab->setEnabled(false); + + menu.move(m_contentWidget->mapToGlobal(pos)); + + QAction *action = menu.exec(); + if (curTab == action) + emit linkActivated(itm->url()); + else if (newTab == action) + OpenPagesManager::instance()->createPage(itm->url()); +} + +void ContentWindow::itemClicked(const QModelIndex &index) +{ + TRACE_OBJ + QHelpContentModel *contentModel = + qobject_cast<QHelpContentModel*>(m_contentWidget->model()); + + if (contentModel) { + if (QHelpContentItem *itm = contentModel->contentItemAt(index)) { + const QUrl &url = itm->url(); + if (url != CentralWidget::instance()->currentSource()) + emit linkActivated(url); + } + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/contentwindow.h b/src/assistant/tools/assistant/contentwindow.h new file mode 100644 index 000000000..b8bdc8fee --- /dev/null +++ b/src/assistant/tools/assistant/contentwindow.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONTENTWINDOW_H +#define CONTENTWINDOW_H + +#include <QtCore/QUrl> +#include <QtCore/QModelIndex> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QHelpEngine; +class QHelpContentItem; +class QHelpContentWidget; + +class ContentWindow : public QWidget +{ + Q_OBJECT + +public: + ContentWindow(); + ~ContentWindow(); + + bool syncToContent(const QUrl &url); + void expandToDepth(int depth); + +signals: + void linkActivated(const QUrl &link); + void escapePressed(); + +private slots: + void showContextMenu(const QPoint &pos); + void expandTOC(); + void itemClicked(const QModelIndex &index); + +private: + void focusInEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + bool eventFilter(QObject *o, QEvent *e); + + QHelpContentWidget * const m_contentWidget; + int m_expandDepth; +}; + +QT_END_NAMESPACE + +#endif // CONTENTWINDOW_H diff --git a/src/assistant/tools/assistant/doc/HOWTO b/src/assistant/tools/assistant/doc/HOWTO new file mode 100644 index 000000000..a0143479e --- /dev/null +++ b/src/assistant/tools/assistant/doc/HOWTO @@ -0,0 +1,16 @@ +How to build/ update a new assistant.qch for Assistant internal help + +- update: + - open assistant.qdocconf, update year and qt version + + - ..\..\..\..\qdoc3\debug\qdoc3.exe assistant.qdocconf + will generate an folder html containing all required stuff + + - cp assistant.qhp to generated html folder + - run qhelpgenerator html\assistant.qhp -o ..\assistant.qch + + - rebuild assistant + +- to test your changes: + - remove assistant.qch in your cache directory + - restart assistant diff --git a/src/assistant/tools/assistant/doc/assistant.qdoc b/src/assistant/tools/assistant/doc/assistant.qdoc new file mode 100644 index 000000000..8bd7432ec --- /dev/null +++ b/src/assistant/tools/assistant/doc/assistant.qdoc @@ -0,0 +1,461 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page assistant.html + \title Qt Assistant + + \chapter Introduction + + This document introduces \e{Qt Assistant}, a tool for presenting on-line + documentation. It also introduces the Qt Reference Documentation which + is accessible using \e{Qt Assistant}, or with a web browser. The document is + divided into the following sections: + + Table of contents: + + \list + \o \l{Introduction} + \o \l{The One-Minute Guide to Using Qt Assistant} + \o \l{Introduction to the Qt Reference Documentation} + \o \l{Qt Assistant in More Detail} + \o \l{Full Text Searching} + \endlist + + \chapter The One-Minute Guide to Using Qt Assistant + + Once you have installed Qt, \QA should be ready to run: + + \list + \o On Windows, \QA is available as a menu option on the Qt menu. + \o On Mac OS X, \QA is installed in the /Developer/Applications/Qt directory. + \o On Unix/Linux, open a terminal, type \c{assistant} and press \key{Enter}. + \endlist + + When you start up \QA, you will be presented with a standard main window + application, with a menu bar and toolbar. Below these, on the left hand + side are navigation windows called \e{Contents}, \e{Index} and \e{Bookmarks}. + On the right, taking up most of the space, is the \e{Documentation} window. + By default, \QA loads the Qt reference documentation along with the manuals + of other Qt tools, like \QD or \QL. + + \QA works in a similar way to a Web browser. If you click hyperlinks + (cross-references), the \e{Documentation} window will present the relevant + page. You can bookmark pages of particular interest and you can click the + \gui{Previous} and \gui{Next} toolbar buttons to navigate within the pages + you have visited. + + Although \QA can be used just like a Web browser to navigate through + the documentation, \QA offers a powerful means of navigation that Web + browsers do not provide. \QA uses an advanced full text search engine + to index all the pages in each compressed help file so that you can + search for particular words and phrases. + + To perform an index search, click the \gui{Index} tab on the Sidebar + (or press \key{Alt+I}). In the \gui{'Look For'} line edit enter a word; + e.g., 'homedirpath'. As you type, words are found and highlighted in a list + beneath the line edit. If the highlighted text matches what you're + looking for, double click it, (or press \key{Enter}) and the + \e{Documentation} window will display the relevant page. You rarely have + to type in the whole word before \QA finds a match. Note that for some + words there may be more than one possible page that is relevant. + + \QA also provides full text searching for finding specific words in + the documentation. To activate the full text search, either press \key(Alt+S) + or click on the \gui{Search} tab in the \e{Documentation} window. Then + enter the term you're looking for and hit the \gui{Search} button. All + documents containing the specified term will then be listed in the list box + below. + + \chapter Introduction to the Qt Reference Documentation + + The documentation for the Qt library is written in-line in the \c + .cpp files by the developers themselves. The documentation team + revises the documentation to ensure that it is accurate and usable, + and to provide quality control. The documentation team also writes the + larger texts, such as the class descriptions that introduce a class + along with the concepts the class uses, as well as introducing the + functions and properties that the class provides. + + The documentation focuses on the API rather than the internals, since + we make great efforts to keep our API consistent and compatible with + each new version, but we may change the internals considerably to improve + performance and enhance functionality. + + The Qt Reference Documentation consists of almost 1,500 HTML pages + (over 2,500 printed pages). The overwhelming majority of pages + document Qt classes. Since developers differ in the way they + think and work we provide a variety of approaches to navigating the + documentation set: + + \list + \i The \menu{Qt's Classes} page lists every class + in Qt's public API, and consists of several hundred classes. + \i The \menu{Qt's Main Classes} page lists the + classes you're most likely to use most often, and provides a much + shorter and more managable list than the All Classes list. + \i The \menu{Grouped Classes} page presents a list + of groups, each of which leads to a list of related classes, for + example, the \menu{Advanced Widgets} list. + \i The \menu{Class Inheritance Hierarchy} page + presents a list of classes in terms of the hierarchy of Qt classes. + \i The \menu{Member Function Index} page lists all the + functions provided by Qt classes, each one with links to the class(es) + in which it appears. + \endlist + + No matter where you find yourself in the Qt documentation, you will + find extensive cross-referencing. Even snippets of example code + contain clickable links, so that for example, if you come across a + class declaration in a code example, the class name will be a + clickable link to the class's documentation. + + In addition to the class documentation some of Qt's modules have + extensive descriptions, and there are many overview documents which + describe various aspects of the Qt library; all these are linked from + the reference documentation home page. There are also two tutorials + and numerous example programs in the examples subdirectory of the Qt + distribution. + + \chapter Qt Assistant in More Detail + + \img assistant-assistant.png + + \section1 Command Line Options + + \QA handles the following command line options: + + \table + \header + \o Command Line Option + \o Brief Description + \row + \o -collectionFile <file.qhc> + \o Uses the specified collection file instead of the default one. + \row + \o -showUrl URL + \o Shows the document referenced by URL. + \row + \o -enableRemoteControl + \o Enables \QA to be remotly controlled. + \row + \o -show <widget> + \o Shows the specified dockwidget which can be "contents", "index", + "bookmarks" or "search". + \row + \o -hide <widget> + \o Hides the specified dockwidget which can be "contents", "index", + "bookmarks" or "search. + \row + \o -activate <widget> + \o Activates the specified dockwidget which can be "contents", + "index", "bookmarks" or "search. + \row + \o -register <doc.qch> + \o Registers the specified compressed help file in the given help + collection. + \row + \o -unregister <doc.qch> + \o Unregisters the specified compressed help file from the given + collection file. + \row + \o -quiet + \o Doesn't show any error, warning or success messages. + \endtable + + \section1 Tool Windows + + \img assistant-dockwidgets.png + + The tool windows provide four ways to navigate the documentation: + + \list + \o The \gui{Contents} window presents a table of contents implemented as a + tree view for the documentation that is available. If you click an item, + its documentation will appear in the \e{Documentation} window. If you double + click an item or click on the control to the left of it, the item's sub-items + will appear. Click a sub-item to make its page appear in the \e{Documentation} + window. Click on the control next to an open item to hide its sub-items. + \o The \gui{Index} window is used to look up key words or phrases. + See \l{The One-Minute Guide to Using Qt Assistant} for how to use this + window. + \o The \gui{Bookmarks} window lists any bookmarks you have made. Double + click a bookmark to make its page appear in the \e{Documentation} window. + The \gui{Bookmarks} window provides a context menu with \gui{Show Item}, + \gui{Delete Item} as well as \gui{Rename Item}. Click in the main menu + \menu{Bookmark|Add Bookmark...} (or press \key{Ctrl+B}) to bookmark the + page that is currently showing in the \e{Documentation} window. Right click + a bookmark in the list to rename or delete the highlighted bookmark. + \endlist + + If you want the \gui{Documentation} window to use as much space as possible, + you can easily group, move or hide the tool windows. To group the windows, + drag one on top of the other and release the mouse. If one or all tool + windows are not shown, press \key{Alt+C}, \key{Alt+I} or \key{Alt+O} to show + the required window. + + The tool windows can be docked into the main window, so you can drag them + to the top, left, right or bottom of \e{Qt Assistant's} window, or you can + drag them outside \QA to float them as independent windows. + + \section1 Documentation Window + + \img assistant-docwindow.png + + The \gui{Documentation} window lets you create a tab for each + documentation page that you view. Click the \gui{Add Tab} button and a new + tab will appear with the page name as the tab's caption. This makes it + convenient to switch between pages when you are working with different + documentation. You can delete a tab by clicking the \gui{Close Tab} button + located on the right side of the \gui{Documentation} window. + + \section1 Toolbars + + \img assistant-toolbar.png + + The main toolbar provides fast access to the most common actions. + + \table + \header \o Action \o Description \o Menu Item \o Shortcut + \row \o \gui{Previous} \o Takes you to the previous page in the history. + \o \menu{Go|Previous} \o \key{Alt+Left Arrow} + \row \o \gui{Next} \o Takes you to the next page in the history. + \o \menu{Go|Next} \o \key{Alt+Right Arrow} + \row \o \gui{Home} + \o Takes you to the home page as specified in the Preferences Dialog. + \o \menu{Go|Home} \o \key{Ctrl+Home}. + \row \o \gui{Sync with Table of Contents} + \o Synchronizes the \gui{Contents} tool window with the page currently + shown in the \gui{Documentation} window. + \o \menu{Go|Sync with Table of Contents} \o + \row \o \gui{Copy} \o Copies any selected text to the clipboard. + \o \menu{Edit|Copy} \o \key{Ctrl+C} + \row \o \gui{Print} \o Opens the \gui{Print} dialog. + \o \menu{File|Print} \o \key{Ctrl+P} + \row \o \gui{Find in Text} \o Opens the \gui{Find Text} dialog. + \o \menu{Edit|Find in Text} \o \key{Ctrl+F} + \row \o \gui{Zoom in} + \o Increases the font size used to display text in the current tab. + \o \menu{View|Zoom in} \o \key{Ctrl++} + \row \o \gui{Zoom out} + \o Decreases the font size used to display text in the current tab. + \o \menu{View|Zoom out} \o \key{Ctrl+-} + \row \o \gui{Normal Size} + \o Resets the font size to its normal size in the current tab. + \o \menu{View|Normal Size} \o \key{Ctrl+0} + \endtable + + \img assistant-address-toolbar.png + + The address toolbar provides a fast way to enter a specific URL for a + documentation file. By default, the address toolbar is not shown, so it + has to be activated via \menu{View|Toolbars|Address Toolbar}. + + \img assistant-filter-toolbar.png + + The filter toolbar allows you to apply a filter to the currently installed + documentation. As with the address toolbar, the filter toolbar is not visible + by default and has to be activated via \menu{View|Toolbars|Filter Toolbar}. + + \section1 Menus + + \section2 File Menu + + \list + \o \menu{File|Page Setup...} invokes a dialog allowing you to define + page layout properties, such as margin sizes, page orientation and paper size. + \o \menu{File|Print Preview...} provides a preview of the printed pages. + \o \menu{File|Print...} opens the \l{#Print Dialog}{\gui{Print} dialog}. + \o \menu{File|New Tab} opens a new empty tab in the \gui{Documentation} + window. + \o \menu{File|Close Tab} closes the current tab of the + \gui{Documentation} window. + \o \menu{File|Exit} closes the \QA application. + \endlist + + \section2 Edit Menu + + \list + \o \menu{Edit|Copy} copies any selected text to the clipboard. + \o \menu{Edit|Find in Text} invokes the \l{#Find Text Control}{\gui{Find Text} + control} at the lower end of the \gui{Documentation} window. + \o \menu{Edit|Find Next} looks for the next occurance of the specified + text in the \gui{Find Text} control. + \o \menu{Edit|Find Previous} looks for the previous occurance of + the specified text in the \l{#Find Text Control}{\gui{Find Text} control}. + \o \menu{Edit|Preferences} invokes the \l{#Preferences Dialog}{\gui{Preferences} dialog}. + \endlist + + \section2 View Menu + + \list + \o \menu{View|Zoom in} increases the font size in the current tab. + \o \menu{View|Zoom out} decreases the font size in the current tab. + \o \menu{View|Normal Size} resets the font size in the current tab. + \o \menu{View|Contents} toggles the display of the \gui{Contents} tool window. + \o \menu{View|Index} toggles the display of the \gui{Index} tool window. + \o \menu{View|Bookmarks} toggles the display of the \gui{Bookmarks} tool window. + \o \menu{View|Search} toggles the display of the Search in the \gui{Documentation} window. + \endlist + + \section2 Go Menu + + \list + \o \menu{Go|Home} goes to the home page. + \o \menu{Go|Back} displays the previous page in the history. + \o \menu{Go|Forward} displays the next page in the history. + \o \menu{Go|Sync with Table of Contents} syncs the \gui{Contents} tool window to the currently shown page. + \o \menu{Go|Next Page} selects the next tab in the \gui{Documentation} window. + \o \menu{Go|Previous Page} selects the previous tab in the \gui{Documentation} window. + \endlist + + \section2 Bookmarks Menu + + \list + \o \menu{Bookmarks|Add} adds the current page to the list of bookmarks. + \endlist + + \section1 Dialogs + + \section2 Print Dialog + + This dialog is platform-specific. It gives access to various printer + options and can be used to print the document shown in the current tab. + + \section2 Preferences Dialog + + \img assistant-preferences-fonts.png + + The \menu{Fonts} page allows you to change the font family and font sizes of the + browser window displaying the documentation or the application itself. + + \img assistant-preferences-filters.png + + The \menu{Filters} page lets you create and remove documentation + filters. To add a new filter, click the \gui{Add} button, specify a + filter name in the pop-up dialog and click \gui{OK}, then select + the filter attributes in the list box on the right hand side. + You can delete a filter by selecting it and clicking the \gui{Remove} + button. + + \img assistant-preferences-documentation.png + + The \menu{Documentation} page lets you install and remove compressed help + files. Click the \gui{Install} button and choose the path of the compressed + help file (*.qch) you would like to install. + To delete a help file, select a documentation set in the list and click + \gui{Remove}. + + \img assistant-preferences-options.png + + The \menu{Options} page lets you specify the homepage \QA will display when + you click the \gui{Home} button in \QA's main user interface. You can specify + the hompage by typing it here or clicking on one of the buttons below the + textbox. \gui{Current Page} sets the currently displayed page as your home + page while \gui{Restore to default} will reset your home page to the default + home page. + + \section1 Find Text Control + + This control is used to find text in the current page. Enter the text you want + to find in the line edit. The search is incremental, meaning that the most + relevant result is shown as you enter characters into the line edit. + + If you check the \gui{Whole words only} checkbox, the search will only consider + whole words; for example, if you search for "spin" with this checkbox checked it will + not match "spinbox", but will match "spin". If you check the \gui{Case sensitive} + checkbox then, for example, "spin" will match "spin" but not "Spin". You can + search forwards or backwards from your current position in the page by clicking + the \gui{Previous} or \gui{Next} buttons. To hide the find control, either click the + \gui{Close} button or hit the \key{Esc} key. + + \section1 Filtering Help Contents + + \QA allows you to install any kind of documentation as long as it is organized + in Qt compressed help files (*.qch). For example, it is possible to install the + Qt reference documentation for Qt 4.4.0 and Qt 4.4.1 at the same time. In many + respects, this is very convenient since only one version of \QA is needed. + However, at the same time it becomes more complicated when performing tasks like + searching the index because nearly every keyword is defined in Qt 4.4.0 as well + as in Qt 4.4.1. This means that \QA will always ask the user to choose which one + should be displayed. + + We use documentation filters to solve this issue. A filter is identified by its + name, and contains a list of filter attributes. An attribute is just a string and + can be freely chosen. Attributes are defined by the documentation itself, this + means that every documentation set usually has one or more attributes. + + For example, the Qt 4.4.0 \QA documentation defines the attributes \c {assistant}, + \c{tools} and \c{4.4.0}, \QD defines \c{designer}, \c{tools} and \c{4.4.0}. + The filter to display all tools would then define only the attribute + \c{tools} since this attribute is part of both documentation sets. + Adding the attribute \c{assistant} to the filter would then only show \QA + documentation since the \QD documentation does not contain this + attribute. Having an empty list of attributes in a filter will match all + documentation; i.e., it is equivalent to requesting unfiltered documentation. + + \section1 Full Text Searching + + \img assistant-search.png + + \QA provides a powerful full text search engine. To search + for certain words or text, click the \gui{Search} tab in the \gui{Documentation} + window. Then enter the text you want to look for and press \key{Enter} + or click the \gui{Search} button. The search is not case sensitive, so, + for example, Foo, fOo and FOO are all treated as the same. The following are + examples of common search patterns: + + \list + \o \c deep -- lists all the documents that contain the word 'deep' + \o \c{deep*} -- lists all the documents that contain a word beginning + with 'deep' + \o \c{deep copy} -- lists all documents that contain both 'deep' \e + and 'copy' + \o \c{"deep copy"} -- list all documents that contain the phrase 'deep copy' + \endlist + + It is also possible to use the \gui{Advanced search} to get more flexibility. + You can specify some words so that hits containing these are excluded from the + result, or you can search for an exact phrase. Searching for similar words will + give results like these: + + \list + \o \c{QStin} -- lists all the documents with titles that are similar, such as \c{QString} + \o \c{QSting} -- lists all the documents with titles that are similar, such as \c{QString} + \o \c{QStrin} -- lists all the documents with titles that are similar, such as \c{QString} + \endlist + + Options can be combined to improve the search results. + + The list of documents found is ordered according to the number of + occurrences of the search text which they contain, with those containing + the highest number of occurrences appearing first. Simply click any + document in the list to display it in the \gui{Documentation} window. + + If the documentation has changed \mdash for example, if documents have been added + or removed \mdash \QA will index them again. +*/ diff --git a/src/assistant/tools/assistant/doc/assistant.qdocconf b/src/assistant/tools/assistant/doc/assistant.qdocconf new file mode 100644 index 000000000..4bd3842d6 --- /dev/null +++ b/src/assistant/tools/assistant/doc/assistant.qdocconf @@ -0,0 +1,16 @@ +include(../../../../qdoc3/test/qt.qdocconf) + +version = + +sourcedirs = $QTDIR/tools/assistant/tools/assistant/doc +imagedirs = $QTDIR/tools/assistant/tools/assistant/doc/images +outputdir = $QTDIR/tools/assistant/tools/assistant/doc/html +project = assistant +description = "Qt Assistant" +HTML.{postheader,address} = "" +HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ + "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \ + "<td width=\"30%\" align=\"left\">Copyright © 2011 Nokia Corporation and/or its subsidiary(-ies)</td>\n" \ + "<td width=\"40%\" align=\"center\">Trademarks</td>\n" \ + "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt 4.8.0</div></td>\n" \ + "</tr></table></div></address>" diff --git a/src/assistant/tools/assistant/doc/assistant.qhp b/src/assistant/tools/assistant/doc/assistant.qhp new file mode 100644 index 000000000..7ea4cdd46 --- /dev/null +++ b/src/assistant/tools/assistant/doc/assistant.qhp @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<QtHelpProject version="1.0"> + <virtualFolder>assistant</virtualFolder> + <namespace>com.trolltech.com.assistantinternal-1.0.0</namespace> + <filterSection> + <files> + <file>assistant.html</file> + <file>classic.css</file> + <file>images/assistant-address-toolbar.png</file> + <file>images/assistant-assistant.png</file> + <file>images/assistant-dockwidgets.png</file> + <file>images/assistant-docwindow.png</file> + <file>images/assistant-filter-toolbar.png</file> + <file>images/assistant-preferences-documentation.png</file> + <file>images/assistant-preferences-filters.png</file> + <file>images/assistant-preferences-fonts.png</file> + <file>images/assistant-preferences-options.png</file> + <file>images/assistant-search.png</file> + <file>images/assistant-toolbar.png</file> + </files> + </filterSection> +</QtHelpProject> diff --git a/src/assistant/tools/assistant/doc/classic.css b/src/assistant/tools/assistant/doc/classic.css new file mode 100644 index 000000000..911354035 --- /dev/null +++ b/src/assistant/tools/assistant/doc/classic.css @@ -0,0 +1,92 @@ +h3.fn,span.fn +{ + margin-left: 1cm; + text-indent: -1cm; +} + +a:link +{ + color: #004faf; + text-decoration: none +} + +a:visited +{ + color: #672967; + text-decoration: none +} + +td.postheader +{ + font-family: sans-serif +} + +tr.address +{ + font-family: sans-serif +} + +body +{ + background: #ffffff; + color: black +} + +table tr.odd { + background: #f0f0f0; + color: black; +} + +table tr.even { + background: #e4e4e4; + color: black; +} + +table.annotated th { + padding: 3px; + text-align: left +} + +table.annotated td { + padding: 3px; +} + +table tr pre +{ + padding-top: none; + padding-bottom: none; + padding-left: none; + padding-right: none; + border: none; + background: none +} + +tr.qt-style +{ + background: #a2c511; + color: black +} + +body pre +{ + padding: 0.2em; + border: #e7e7e7 1px solid; + background: #f1f1f1; + color: black +} + +span.preprocessor, span.preprocessor a +{ + color: darkblue; +} + +span.comment +{ + color: darkred; + font-style: italic +} + +span.string,span.char +{ + color: darkgreen; +} diff --git a/src/assistant/tools/assistant/doc/images/assistant-address-toolbar.png b/src/assistant/tools/assistant/doc/images/assistant-address-toolbar.png Binary files differnew file mode 100644 index 000000000..847b7debb --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-address-toolbar.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-assistant.png b/src/assistant/tools/assistant/doc/images/assistant-assistant.png Binary files differnew file mode 100644 index 000000000..1ff5cc976 --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-assistant.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-dockwidgets.png b/src/assistant/tools/assistant/doc/images/assistant-dockwidgets.png Binary files differnew file mode 100644 index 000000000..17bc064c5 --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-dockwidgets.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-docwindow.png b/src/assistant/tools/assistant/doc/images/assistant-docwindow.png Binary files differnew file mode 100644 index 000000000..c5bac581f --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-docwindow.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-examples.png b/src/assistant/tools/assistant/doc/images/assistant-examples.png Binary files differnew file mode 100644 index 000000000..47c01bcda --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-examples.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-filter-toolbar.png b/src/assistant/tools/assistant/doc/images/assistant-filter-toolbar.png Binary files differnew file mode 100644 index 000000000..4e89a79b9 --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-filter-toolbar.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-preferences-documentation.png b/src/assistant/tools/assistant/doc/images/assistant-preferences-documentation.png Binary files differnew file mode 100644 index 000000000..790fd9a1a --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-preferences-documentation.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-preferences-filters.png b/src/assistant/tools/assistant/doc/images/assistant-preferences-filters.png Binary files differnew file mode 100644 index 000000000..7453dd66d --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-preferences-filters.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-preferences-fonts.png b/src/assistant/tools/assistant/doc/images/assistant-preferences-fonts.png Binary files differnew file mode 100644 index 000000000..d42d190d0 --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-preferences-fonts.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-preferences-options.png b/src/assistant/tools/assistant/doc/images/assistant-preferences-options.png Binary files differnew file mode 100644 index 000000000..d6624151a --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-preferences-options.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-search.png b/src/assistant/tools/assistant/doc/images/assistant-search.png Binary files differnew file mode 100644 index 000000000..ef75c3329 --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-search.png diff --git a/src/assistant/tools/assistant/doc/images/assistant-toolbar.png b/src/assistant/tools/assistant/doc/images/assistant-toolbar.png Binary files differnew file mode 100644 index 000000000..1b41825c6 --- /dev/null +++ b/src/assistant/tools/assistant/doc/images/assistant-toolbar.png diff --git a/src/assistant/tools/assistant/filternamedialog.cpp b/src/assistant/tools/assistant/filternamedialog.cpp new file mode 100644 index 000000000..1d9563d2f --- /dev/null +++ b/src/assistant/tools/assistant/filternamedialog.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include <QtGui/QPushButton> + +#include "filternamedialog.h" + +QT_BEGIN_NAMESPACE + +FilterNameDialog::FilterNameDialog(QWidget *parent) + : QDialog(parent) +{ + TRACE_OBJ + m_ui.setupUi(this); + connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), + SIGNAL(clicked()), this, SLOT(accept())); + connect(m_ui.buttonBox->button(QDialogButtonBox::Cancel), + SIGNAL(clicked()), this, SLOT(reject())); + connect(m_ui.lineEdit, SIGNAL(textChanged(QString)), + this, SLOT(updateOkButton())); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true); + +} + +QString FilterNameDialog::filterName() const +{ + TRACE_OBJ + return m_ui.lineEdit->text(); +} + +void FilterNameDialog::updateOkButton() +{ + TRACE_OBJ + m_ui.buttonBox->button(QDialogButtonBox::Ok) + ->setDisabled(m_ui.lineEdit->text().isEmpty()); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/filternamedialog.h b/src/assistant/tools/assistant/filternamedialog.h new file mode 100644 index 000000000..35575421e --- /dev/null +++ b/src/assistant/tools/assistant/filternamedialog.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FILTERNAMEDIALOG_H +#define FILTERNAMEDIALOG_H + +#include <QtGui/QDialog> +#include "ui_filternamedialog.h" + +QT_BEGIN_NAMESPACE + +class FilterNameDialog : public QDialog +{ + Q_OBJECT + +public: + FilterNameDialog(QWidget *parent = 0); + QString filterName() const; + +private slots: + void updateOkButton(); + +private: + Ui::FilterNameDialogClass m_ui; +}; + +QT_END_NAMESPACE + +#endif // FILTERNAMEDIALOG_H diff --git a/src/assistant/tools/assistant/filternamedialog.ui b/src/assistant/tools/assistant/filternamedialog.ui new file mode 100644 index 000000000..755a93479 --- /dev/null +++ b/src/assistant/tools/assistant/filternamedialog.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>FilterNameDialogClass</class> + <widget class="QDialog" name="FilterNameDialogClass" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>312</width> + <height>95</height> + </rect> + </property> + <property name="windowTitle" > + <string>Add Filter Name</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Filter Name:</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2" > + <widget class="QLineEdit" name="lineEdit" /> + </item> + <item row="1" column="0" colspan="3" > + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="2" > + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <resources/> + <connections/> +</ui> diff --git a/src/assistant/tools/assistant/findwidget.cpp b/src/assistant/tools/assistant/findwidget.cpp new file mode 100644 index 000000000..d45ed30b0 --- /dev/null +++ b/src/assistant/tools/assistant/findwidget.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" +#include "findwidget.h" + +#include <QtGui/QApplication> +#include <QtGui/QCheckBox> +#include <QtGui/QHideEvent> +#include <QtGui/QKeyEvent> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QToolButton> + +QT_BEGIN_NAMESPACE + +FindWidget::FindWidget(QWidget *parent) + : QWidget(parent) + , appPalette(qApp->palette()) +{ + TRACE_OBJ + installEventFilter(this); + QHBoxLayout *hboxLayout = new QHBoxLayout(this); + QString resourcePath = QLatin1String(":/trolltech/assistant/images/"); + +#ifndef Q_OS_MAC + hboxLayout->setMargin(0); + hboxLayout->setSpacing(6); + resourcePath.append(QLatin1String("win")); +#else + resourcePath.append(QLatin1String("mac")); +#endif + + toolClose = setupToolButton(QLatin1String(""), + resourcePath + QLatin1String("/closetab.png")); + hboxLayout->addWidget(toolClose); + connect(toolClose, SIGNAL(clicked()), SLOT(hide())); + + editFind = new QLineEdit(this); + hboxLayout->addWidget(editFind); + editFind->setMinimumSize(QSize(150, 0)); + connect(editFind, SIGNAL(textChanged(QString)), this, + SLOT(textChanged(QString))); + connect(editFind, SIGNAL(returnPressed()), this, SIGNAL(findNext())); + connect(editFind, SIGNAL(textChanged(QString)), this, SLOT(updateButtons())); + + toolPrevious = setupToolButton(tr("Previous"), + resourcePath + QLatin1String("/previous.png")); + connect(toolPrevious, SIGNAL(clicked()), this, SIGNAL(findPrevious())); + + hboxLayout->addWidget(toolPrevious); + + toolNext = setupToolButton(tr("Next"), + resourcePath + QLatin1String("/next.png")); + hboxLayout->addWidget(toolNext); + connect(toolNext, SIGNAL(clicked()), this, SIGNAL(findNext())); + + checkCase = new QCheckBox(tr("Case Sensitive"), this); + hboxLayout->addWidget(checkCase); + + labelWrapped = new QLabel(this); + labelWrapped->setScaledContents(true); + labelWrapped->setTextFormat(Qt::RichText); + labelWrapped->setMinimumSize(QSize(0, 20)); + labelWrapped->setMaximumSize(QSize(105, 20)); + labelWrapped->setAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter); + labelWrapped->setText(tr("<img src=\":/trolltech/assistant/images/wrap.png\"" + "> Search wrapped")); + hboxLayout->addWidget(labelWrapped); + + QSpacerItem *spacerItem = new QSpacerItem(20, 20, QSizePolicy::Expanding, + QSizePolicy::Minimum); + hboxLayout->addItem(spacerItem); + setMinimumWidth(minimumSizeHint().width()); + labelWrapped->hide(); + + updateButtons(); +} + +FindWidget::~FindWidget() +{ + TRACE_OBJ +} + +void FindWidget::show() +{ + TRACE_OBJ + QWidget::show(); + editFind->selectAll(); + editFind->setFocus(Qt::ShortcutFocusReason); +} + +void FindWidget::showAndClear() +{ + TRACE_OBJ + show(); + editFind->clear(); +} + +QString FindWidget::text() const +{ + TRACE_OBJ + return editFind->text(); +} + +bool FindWidget::caseSensitive() const +{ + TRACE_OBJ + return checkCase->isChecked(); +} + +void FindWidget::setPalette(bool found) +{ + TRACE_OBJ + QPalette palette = editFind->palette(); + palette.setColor(QPalette::Active, QPalette::Base, found ? Qt::white + : QColor(255, 102, 102)); + editFind->setPalette(palette); +} + +void FindWidget::setTextWrappedVisible(bool visible) +{ + TRACE_OBJ + labelWrapped->setVisible(visible); +} + +void FindWidget::hideEvent(QHideEvent* event) +{ + TRACE_OBJ +#if !defined(QT_NO_WEBKIT) + // TODO: remove this once webkit supports setting the palette + if (!event->spontaneous()) + qApp->setPalette(appPalette); +#else + Q_UNUSED(event); +#endif +} + +void FindWidget::showEvent(QShowEvent* event) +{ + TRACE_OBJ +#if !defined(QT_NO_WEBKIT) + // TODO: remove this once webkit supports setting the palette + if (!event->spontaneous()) { + QPalette p = appPalette; + p.setColor(QPalette::Inactive, QPalette::Highlight, + p.color(QPalette::Active, QPalette::Highlight)); + p.setColor(QPalette::Inactive, QPalette::HighlightedText, + p.color(QPalette::Active, QPalette::HighlightedText)); + qApp->setPalette(p); + } +#else + Q_UNUSED(event); +#endif +} + +void FindWidget::updateButtons() +{ + TRACE_OBJ + const bool enable = !editFind->text().isEmpty(); + toolNext->setEnabled(enable); + toolPrevious->setEnabled(enable); +} + +void FindWidget::textChanged(const QString &text) +{ + TRACE_OBJ + emit find(text, true, true); +} + +bool FindWidget::eventFilter(QObject *object, QEvent *e) +{ + TRACE_OBJ + if (e->type() == QEvent::KeyPress) { + if ((static_cast<QKeyEvent*>(e))->key() == Qt::Key_Escape) { + hide(); + emit escapePressed(); + } + } + return QWidget::eventFilter(object, e); +} + +QToolButton* FindWidget::setupToolButton(const QString &text, const QString &icon) +{ + TRACE_OBJ + QToolButton *toolButton = new QToolButton(this); + + toolButton->setText(text); + toolButton->setAutoRaise(true); + toolButton->setIcon(QIcon(icon)); + toolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + return toolButton; +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/findwidget.h b/src/assistant/tools/assistant/findwidget.h new file mode 100644 index 000000000..d3be0f587 --- /dev/null +++ b/src/assistant/tools/assistant/findwidget.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FINDWIDGET_H +#define FINDWIDGET_H + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QCheckBox; +class QLabel; +class QLineEdit; +class QToolButton; + +class FindWidget : public QWidget +{ + Q_OBJECT +public: + FindWidget(QWidget *parent = 0); + ~FindWidget(); + + void show(); + void showAndClear(); + + QString text() const; + bool caseSensitive() const; + + void setPalette(bool found); + void setTextWrappedVisible(bool visible); + +signals: + void findNext(); + void findPrevious(); + void escapePressed(); + void find(const QString &text, bool forward, bool incremental); + +protected: + void hideEvent(QHideEvent* event); + void showEvent(QShowEvent * event); + +private slots: + void updateButtons(); + void textChanged(const QString &text); + +private: + bool eventFilter(QObject *object, QEvent *e); + QToolButton* setupToolButton(const QString &text, const QString &icon); + +private: + QPalette appPalette; + + QLineEdit *editFind; + QCheckBox *checkCase; + QLabel *labelWrapped; + QToolButton *toolNext; + QToolButton *toolClose; + QToolButton *toolPrevious; +}; + +QT_END_NAMESPACE + +#endif // FINDWIDGET_H diff --git a/src/assistant/tools/assistant/globalactions.cpp b/src/assistant/tools/assistant/globalactions.cpp new file mode 100644 index 000000000..7fc59ebcd --- /dev/null +++ b/src/assistant/tools/assistant/globalactions.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "globalactions.h" + +#include "centralwidget.h" +#include "helpviewer.h" +#include "tracer.h" + +#include <QtGui/QAction> +#include <QtGui/QMenu> + +#if !defined(QT_NO_WEBKIT) +#include <QtWebKit/QWebHistory> +#endif + +GlobalActions *GlobalActions::instance(QObject *parent) +{ + Q_ASSERT(!m_instance != !parent); + if (!m_instance) + m_instance = new GlobalActions(parent); + return m_instance; +} + +GlobalActions::GlobalActions(QObject *parent) : QObject(parent) +{ + TRACE_OBJ + + // TODO: Put resource path in misc class + QString resourcePath = QLatin1String(":/trolltech/assistant/images/"); +#ifdef Q_OS_MAC + resourcePath.append(QLatin1String("mac")); +#else + resourcePath.append(QLatin1String("win")); +#endif + CentralWidget *centralWidget = CentralWidget::instance(); + + m_backAction = new QAction(tr("&Back"), parent); + m_backAction->setEnabled(false); + m_backAction->setShortcuts(QKeySequence::Back); + m_backAction->setIcon(QIcon(resourcePath + QLatin1String("/previous.png"))); + connect(m_backAction, SIGNAL(triggered()), centralWidget, SLOT(backward())); + m_actionList << m_backAction; + + m_nextAction = new QAction(tr("&Forward"), parent); + m_nextAction->setPriority(QAction::LowPriority); + m_nextAction->setEnabled(false); + m_nextAction->setShortcuts(QKeySequence::Forward); + m_nextAction->setIcon(QIcon(resourcePath + QLatin1String("/next.png"))); + connect(m_nextAction, SIGNAL(triggered()), centralWidget, SLOT(forward())); + m_actionList << m_nextAction; + + setupNavigationMenus(m_backAction, m_nextAction, centralWidget); + + m_homeAction = new QAction(tr("&Home"), parent); + m_homeAction->setShortcut(tr("ALT+Home")); + m_homeAction->setIcon(QIcon(resourcePath + QLatin1String("/home.png"))); + connect(m_homeAction, SIGNAL(triggered()), centralWidget, SLOT(home())); + m_actionList << m_homeAction; + + QAction *separator = new QAction(parent); + separator->setSeparator(true); + m_actionList << separator; + + m_zoomInAction = new QAction(tr("Zoom &in"), parent); + m_zoomInAction->setPriority(QAction::LowPriority); + m_zoomInAction->setIcon(QIcon(resourcePath + QLatin1String("/zoomin.png"))); + m_zoomInAction->setShortcut(QKeySequence::ZoomIn); + connect(m_zoomInAction, SIGNAL(triggered()), centralWidget, SLOT(zoomIn())); + m_actionList << m_zoomInAction; + + m_zoomOutAction = new QAction(tr("Zoom &out"), parent); + m_zoomOutAction->setPriority(QAction::LowPriority); + m_zoomOutAction->setIcon(QIcon(resourcePath + QLatin1String("/zoomout.png"))); + m_zoomOutAction->setShortcut(QKeySequence::ZoomOut); + connect(m_zoomOutAction, SIGNAL(triggered()), centralWidget, SLOT(zoomOut())); + m_actionList << m_zoomOutAction; + + separator = new QAction(parent); + separator->setSeparator(true); + m_actionList << separator; + + m_copyAction = new QAction(tr("&Copy selected Text"), parent); + m_copyAction->setPriority(QAction::LowPriority); + m_copyAction->setIconText("&Copy"); + m_copyAction->setIcon(QIcon(resourcePath + QLatin1String("/editcopy.png"))); + m_copyAction->setShortcuts(QKeySequence::Copy); + m_copyAction->setEnabled(false); + connect(m_copyAction, SIGNAL(triggered()), centralWidget, SLOT(copy())); + m_actionList << m_copyAction; + + m_printAction = new QAction(tr("&Print..."), parent); + m_printAction->setPriority(QAction::LowPriority); + m_printAction->setIcon(QIcon(resourcePath + QLatin1String("/print.png"))); + m_printAction->setShortcut(QKeySequence::Print); + connect(m_printAction, SIGNAL(triggered()), centralWidget, SLOT(print())); + m_actionList << m_printAction; + + m_findAction = new QAction(tr("&Find in Text..."), parent); + m_findAction->setIconText(tr("&Find")); + m_findAction->setIcon(QIcon(resourcePath + QLatin1String("/find.png"))); + m_findAction->setShortcuts(QKeySequence::Find); + connect(m_findAction, SIGNAL(triggered()), centralWidget, SLOT(showTextSearch())); + m_actionList << m_findAction; + +#ifdef Q_WS_X11 + m_backAction->setIcon(QIcon::fromTheme("go-previous" , m_backAction->icon())); + m_nextAction->setIcon(QIcon::fromTheme("go-next" , m_nextAction->icon())); + m_zoomInAction->setIcon(QIcon::fromTheme("zoom-in" , m_zoomInAction->icon())); + m_zoomOutAction->setIcon(QIcon::fromTheme("zoom-out" , m_zoomOutAction->icon())); + m_copyAction->setIcon(QIcon::fromTheme("edit-copy" , m_copyAction->icon())); + m_findAction->setIcon(QIcon::fromTheme("edit-find" , m_findAction->icon())); + m_homeAction->setIcon(QIcon::fromTheme("go-home" , m_homeAction->icon())); + m_printAction->setIcon(QIcon::fromTheme("document-print" , m_printAction->icon())); +#endif +} + +void GlobalActions::updateActions() +{ + TRACE_OBJ + CentralWidget *centralWidget = CentralWidget::instance(); + m_copyAction->setEnabled(centralWidget->hasSelection()); + m_nextAction->setEnabled(centralWidget->isForwardAvailable()); + m_backAction->setEnabled(centralWidget->isBackwardAvailable()); +} + +void GlobalActions::setCopyAvailable(bool available) +{ + TRACE_OBJ + m_copyAction->setEnabled(available); +} + +#if !defined(QT_NO_WEBKIT) + +void GlobalActions::slotAboutToShowBackMenu() +{ + TRACE_OBJ + m_backMenu->clear(); + if (QWebHistory *history = CentralWidget::instance()->currentHelpViewer()->history()) { + const int currentItemIndex = history->currentItemIndex(); + QList<QWebHistoryItem> items = history->backItems(history->count()); + for (int i = items.count() - 1; i >= 0; --i) { + QAction *action = new QAction(this); + action->setText(items.at(i).title()); + action->setData(-1 * (currentItemIndex - i)); + m_backMenu->addAction(action); + } + } +} + +void GlobalActions::slotAboutToShowNextMenu() +{ + TRACE_OBJ + m_nextMenu->clear(); + if (QWebHistory *history = CentralWidget::instance()->currentHelpViewer()->history()) { + const int count = history->count(); + QList<QWebHistoryItem> items = history->forwardItems(count); + for (int i = 0; i < items.count(); ++i) { + QAction *action = new QAction(this); + action->setData(count - i); + action->setText(items.at(i).title()); + m_nextMenu->addAction(action); + } + } +} + +void GlobalActions::slotOpenActionUrl(QAction *action) +{ + TRACE_OBJ + if (HelpViewer* viewer = CentralWidget::instance()->currentHelpViewer()) { + const int offset = action->data().toInt(); + QWebHistory *history = viewer->history(); + if (offset > 0) { + history->goToItem(history->forwardItems(history->count() + - offset + 1).back()); // forward + } else if (offset < 0) { + history->goToItem(history->backItems(-1 * offset).first()); // back + } + } +} + +#endif + +void GlobalActions::setupNavigationMenus(QAction *back, QAction *next, + QWidget *parent) +{ +#if !defined(QT_NO_WEBKIT) + m_backMenu = new QMenu(parent); + connect(m_backMenu, SIGNAL(aboutToShow()), this, + SLOT(slotAboutToShowBackMenu())); + connect(m_backMenu, SIGNAL(triggered(QAction*)), this, + SLOT(slotOpenActionUrl(QAction*))); + back->setMenu(m_backMenu); + + m_nextMenu = new QMenu(parent); + connect(m_nextMenu, SIGNAL(aboutToShow()), this, + SLOT(slotAboutToShowNextMenu())); + connect(m_nextMenu, SIGNAL(triggered(QAction*)), this, + SLOT(slotOpenActionUrl(QAction*))); + next->setMenu(m_nextMenu); +#else + Q_UNUSED(back) + Q_UNUSED(next) + Q_UNUSED(parent) +#endif +} + +GlobalActions *GlobalActions::m_instance = 0; diff --git a/src/assistant/tools/assistant/globalactions.h b/src/assistant/tools/assistant/globalactions.h new file mode 100644 index 000000000..a9059a9a4 --- /dev/null +++ b/src/assistant/tools/assistant/globalactions.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GLOBALACTION_H +#define GLOBALACTION_H + +#include <QtCore/QList> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QAction; +class QMenu; + +class GlobalActions : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(GlobalActions) +public: + static GlobalActions *instance(QObject *parent = 0); + + QList<QAction *> actionList() const { return m_actionList; } + QAction *backAction() const { return m_backAction; } + QAction *nextAction() const { return m_nextAction; } + QAction *homeAction() const { return m_homeAction; } + QAction *zoomInAction() const { return m_zoomInAction; } + QAction *zoomOutAction() const { return m_zoomOutAction; } + QAction *copyAction() const { return m_copyAction; } + QAction *printAction() const { return m_printAction; } + QAction *findAction() const { return m_findAction; } + + Q_SLOT void updateActions(); + Q_SLOT void setCopyAvailable(bool available); + +#if !defined(QT_NO_WEBKIT) +private slots: + void slotAboutToShowBackMenu(); + void slotAboutToShowNextMenu(); + void slotOpenActionUrl(QAction *action); +#endif + +private: + void setupNavigationMenus(QAction *back, QAction *next, QWidget *parent); + +private: + GlobalActions(QObject *parent); + + static GlobalActions *m_instance; + + QAction *m_backAction; + QAction *m_nextAction; + QAction *m_homeAction; + QAction *m_zoomInAction; + QAction *m_zoomOutAction; + QAction *m_copyAction; + QAction *m_printAction; + QAction *m_findAction; + + QList<QAction *> m_actionList; + + QMenu *m_backMenu; + QMenu *m_nextMenu; +}; + +QT_END_NAMESPACE + +#endif // GLOBALACTION_H diff --git a/src/assistant/tools/assistant/helpenginewrapper.cpp b/src/assistant/tools/assistant/helpenginewrapper.cpp new file mode 100644 index 000000000..4c7ff5b30 --- /dev/null +++ b/src/assistant/tools/assistant/helpenginewrapper.cpp @@ -0,0 +1,844 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include "helpenginewrapper.h" +#include "../shared/collectionconfiguration.h" + +#include <QtCore/QDateTime> +#include <QtCore/QFileInfo> +#include <QtCore/QFileSystemWatcher> +#include <QtCore/QPair> +#include <QtCore/QSharedPointer> +#include <QtCore/QTimer> +#include <QtHelp/QHelpContentModel> +#include <QtHelp/QHelpEngine> +#include <QtHelp/QHelpIndexModel> +#include <QtHelp/QHelpSearchEngine> + +QT_BEGIN_NAMESPACE + +namespace { + const QString Unfiltered; + const QString AppFontKey(QLatin1String("appFont")); + const QString AppWritingSystemKey(QLatin1String("appWritingSystem")); + const QString BookmarksKey(QLatin1String("Bookmarks")); + const QString BrowserFontKey(QLatin1String("browserFont")); + const QString BrowserWritingSystemKey(QLatin1String("browserWritingSystem")); + const QString HomePageKey(QLatin1String("homepage")); + const QString MainWindowKey(QLatin1String("MainWindow")); + const QString MainWindowGeometryKey(QLatin1String("MainWindowGeometry")); + const QString SearchWasAttachedKey(QLatin1String("SearchWasAttached")); + const QString StartOptionKey(QLatin1String("StartOption")); + const QString UseAppFontKey(QLatin1String("useAppFont")); + const QString UseBrowserFontKey(QLatin1String("useBrowserFont")); + const QString VersionKey(QString(QLatin1String("qtVersion%1$$$%2")). + arg(QLatin1String(QT_VERSION_STR))); + const QString ShowTabsKey(QLatin1String("showTabs")); +} // anonymous namespace + +class TimeoutForwarder : public QObject +{ + Q_OBJECT +public: + TimeoutForwarder(const QString &fileName); +private slots: + void forward(); +private: + friend class HelpEngineWrapperPrivate; + + const QString m_fileName; +}; + +class HelpEngineWrapperPrivate : public QObject +{ + Q_OBJECT + friend class HelpEngineWrapper; + friend class TimeoutForwarder; +private slots: + void qchFileChanged(const QString &fileName); + +signals: + void documentationRemoved(const QString &namespaceName); + void documentationUpdated(const QString &namespaceName); + +private: + HelpEngineWrapperPrivate(const QString &collectionFile); + + void initFileSystemWatchers(); + void checkDocFilesWatched(); + void qchFileChanged(const QString &fileName, bool fromTimeout); + + static const int UpdateGracePeriod = 2000; + + QHelpEngine * const m_helpEngine; + QFileSystemWatcher * const m_qchWatcher; + typedef QPair<QDateTime, QSharedPointer<TimeoutForwarder> > RecentSignal; + QMap<QString, RecentSignal> m_recentQchUpdates; +}; + +const QString HelpEngineWrapper::TrUnfiltered = HelpEngineWrapper::tr("Unfiltered"); + +HelpEngineWrapper *HelpEngineWrapper::helpEngineWrapper = 0; + +HelpEngineWrapper &HelpEngineWrapper::instance(const QString &collectionFile) +{ + TRACE_OBJ + /* + * Note that this Singleton cannot be static, because it has to be + * deleted before the QApplication. + */ + if (helpEngineWrapper == 0) + helpEngineWrapper = new HelpEngineWrapper(collectionFile); + return *helpEngineWrapper; +} + +void HelpEngineWrapper::removeInstance() +{ + TRACE_OBJ + delete helpEngineWrapper; + helpEngineWrapper = 0; +} + +HelpEngineWrapper::HelpEngineWrapper(const QString &collectionFile) + : d(new HelpEngineWrapperPrivate(collectionFile)) +{ + TRACE_OBJ + + /* + * Otherwise we will waste time if several new docs are found, + * because we will start to index them, only to be interrupted + * by the next request. Also, there is a nasty SQLITE bug that will + * cause the application to hang for minutes in that case. + * This call is reverted by initialDocSetupDone(), which must be + * called after the new docs have been installed. + */ + disconnect(d->m_helpEngine, SIGNAL(setupFinished()), + searchEngine(), SLOT(indexDocumentation())); + + connect(d, SIGNAL(documentationRemoved(QString)), + this, SIGNAL(documentationRemoved(QString))); + connect(d, SIGNAL(documentationUpdated(QString)), + this, SIGNAL(documentationUpdated(QString))); + connect(d->m_helpEngine, SIGNAL(currentFilterChanged(QString)), + this, SLOT(handleCurrentFilterChanged(QString))); + connect(d->m_helpEngine, SIGNAL(setupFinished()), + this, SIGNAL(setupFinished())); +} + +HelpEngineWrapper::~HelpEngineWrapper() +{ + TRACE_OBJ + const QStringList &namespaces = d->m_helpEngine->registeredDocumentations(); + foreach (const QString &nameSpace, namespaces) { + const QString &docFile + = d->m_helpEngine->documentationFileName(nameSpace); + d->m_qchWatcher->removePath(docFile); + } + + delete d; +} + +void HelpEngineWrapper::initialDocSetupDone() +{ + TRACE_OBJ + connect(d->m_helpEngine, SIGNAL(setupFinished()), + searchEngine(), SLOT(indexDocumentation())); + setupData(); +} + +QHelpSearchEngine *HelpEngineWrapper::searchEngine() const +{ + TRACE_OBJ + return d->m_helpEngine->searchEngine(); +} + +QHelpContentModel *HelpEngineWrapper::contentModel() const +{ + TRACE_OBJ + return d->m_helpEngine->contentModel(); +} + +QHelpIndexModel *HelpEngineWrapper::indexModel() const +{ + TRACE_OBJ + return d->m_helpEngine->indexModel(); +} + +QHelpContentWidget *HelpEngineWrapper::contentWidget() +{ + TRACE_OBJ + return d->m_helpEngine->contentWidget(); +} + +QHelpIndexWidget *HelpEngineWrapper::indexWidget() +{ + TRACE_OBJ + return d->m_helpEngine->indexWidget(); +} + +const QStringList HelpEngineWrapper::registeredDocumentations() const +{ + TRACE_OBJ + return d->m_helpEngine->registeredDocumentations(); +} + +const QString HelpEngineWrapper::collectionFile() const +{ + TRACE_OBJ + return d->m_helpEngine->collectionFile(); +} + +bool HelpEngineWrapper::registerDocumentation(const QString &docFile) +{ + TRACE_OBJ + d->checkDocFilesWatched(); + if (!d->m_helpEngine->registerDocumentation(docFile)) + return false; + d->m_qchWatcher->addPath(docFile); + d->checkDocFilesWatched(); + return true; +} + +bool HelpEngineWrapper::unregisterDocumentation(const QString &namespaceName) +{ + TRACE_OBJ + d->checkDocFilesWatched(); + const QString &file = d->m_helpEngine->documentationFileName(namespaceName); + if (!d->m_helpEngine->unregisterDocumentation(namespaceName)) + return false; + d->m_qchWatcher->removePath(file); + d->checkDocFilesWatched(); + return true; +} + +bool HelpEngineWrapper::setupData() +{ + TRACE_OBJ + return d->m_helpEngine->setupData(); +} + +bool HelpEngineWrapper::addCustomFilter(const QString &filterName, + const QStringList &attributes) +{ + TRACE_OBJ + return d->m_helpEngine->addCustomFilter(filterName, attributes); +} + +bool HelpEngineWrapper::removeCustomFilter(const QString &filterName) +{ + TRACE_OBJ + return d->m_helpEngine->removeCustomFilter(filterName); +} + +void HelpEngineWrapper::setCurrentFilter(const QString ¤tFilter) +{ + TRACE_OBJ + const QString &filter + = currentFilter == TrUnfiltered ? Unfiltered : currentFilter; + d->m_helpEngine->setCurrentFilter(filter); +} + +const QString HelpEngineWrapper::currentFilter() const +{ + TRACE_OBJ + const QString &filter = d->m_helpEngine->currentFilter(); + return filter == Unfiltered ? TrUnfiltered : filter; +} + +const QStringList HelpEngineWrapper::customFilters() const +{ + TRACE_OBJ + QStringList filters = d->m_helpEngine->customFilters(); + filters.removeOne(Unfiltered); + filters.prepend(TrUnfiltered); + return filters; +} + +QUrl HelpEngineWrapper::findFile(const QUrl &url) const +{ + TRACE_OBJ + return d->m_helpEngine->findFile(url); +} + +QByteArray HelpEngineWrapper::fileData(const QUrl &url) const +{ + TRACE_OBJ + return d->m_helpEngine->fileData(url); +} + +QMap<QString, QUrl> HelpEngineWrapper::linksForIdentifier(const QString &id) const +{ + TRACE_OBJ + return d->m_helpEngine->linksForIdentifier(id); +} + +const QStringList HelpEngineWrapper::filterAttributes() const +{ + TRACE_OBJ + return d->m_helpEngine->filterAttributes(); +} + +const QStringList HelpEngineWrapper::filterAttributes(const QString &filterName) const +{ + TRACE_OBJ + return d->m_helpEngine->filterAttributes(filterName); +} + +QString HelpEngineWrapper::error() const +{ + TRACE_OBJ + return d->m_helpEngine->error(); +} + +const QStringList HelpEngineWrapper::qtDocInfo(const QString &component) const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(VersionKey.arg(component)).toString(). + split(CollectionConfiguration::ListSeparator); +} + +void HelpEngineWrapper::setQtDocInfo(const QString &component, + const QStringList &doc) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(VersionKey.arg(component), + doc.join(CollectionConfiguration::ListSeparator)); +} + +const QStringList HelpEngineWrapper::lastShownPages() const +{ + TRACE_OBJ + return CollectionConfiguration::lastShownPages(*d->m_helpEngine); +} + +void HelpEngineWrapper::setLastShownPages(const QStringList &lastShownPages) +{ + TRACE_OBJ + CollectionConfiguration::setLastShownPages(*d->m_helpEngine, lastShownPages); +} + +const QStringList HelpEngineWrapper::lastZoomFactors() const +{ + TRACE_OBJ + return CollectionConfiguration::lastZoomFactors(*d->m_helpEngine); +} + +void HelpEngineWrapper::setLastZoomFactors(const QStringList &lastZoomFactors) +{ + TRACE_OBJ + CollectionConfiguration::setLastZoomFactors(*d->m_helpEngine, lastZoomFactors); +} + +const QString HelpEngineWrapper::cacheDir() const +{ + TRACE_OBJ + return CollectionConfiguration::cacheDir(*d->m_helpEngine); +} + +bool HelpEngineWrapper::cacheDirIsRelativeToCollection() const +{ + TRACE_OBJ + return CollectionConfiguration::cacheDirIsRelativeToCollection(*d->m_helpEngine); +} + +void HelpEngineWrapper::setCacheDir(const QString &cacheDir, + bool relativeToCollection) +{ + TRACE_OBJ + CollectionConfiguration::setCacheDir(*d->m_helpEngine, cacheDir, + relativeToCollection); +} + +bool HelpEngineWrapper::filterFunctionalityEnabled() const +{ + TRACE_OBJ + return CollectionConfiguration::filterFunctionalityEnabled(*d->m_helpEngine); +} + +void HelpEngineWrapper::setFilterFunctionalityEnabled(bool enabled) +{ + TRACE_OBJ + CollectionConfiguration::setFilterFunctionalityEnabled(*d->m_helpEngine, + enabled); +} + +bool HelpEngineWrapper::filterToolbarVisible() const +{ + TRACE_OBJ + return CollectionConfiguration::filterToolbarVisible(*d->m_helpEngine); +} + +void HelpEngineWrapper::setFilterToolbarVisible(bool visible) +{ + TRACE_OBJ + CollectionConfiguration::setFilterToolbarVisible(*d->m_helpEngine, visible); +} + +bool HelpEngineWrapper::addressBarEnabled() const +{ + TRACE_OBJ + return CollectionConfiguration::addressBarEnabled(*d->m_helpEngine); +} + +void HelpEngineWrapper::setAddressBarEnabled(bool enabled) +{ + TRACE_OBJ + CollectionConfiguration::setAddressBarEnabled(*d->m_helpEngine, enabled); +} + +bool HelpEngineWrapper::addressBarVisible() const +{ + TRACE_OBJ + return CollectionConfiguration::addressBarVisible(*d->m_helpEngine); +} + +void HelpEngineWrapper::setAddressBarVisible(bool visible) +{ + TRACE_OBJ + CollectionConfiguration::setAddressBarVisible(*d->m_helpEngine, visible); +} + +bool HelpEngineWrapper::documentationManagerEnabled() const +{ + TRACE_OBJ + return CollectionConfiguration::documentationManagerEnabled(*d->m_helpEngine); +} + +void HelpEngineWrapper::setDocumentationManagerEnabled(bool enabled) +{ + TRACE_OBJ + CollectionConfiguration::setDocumentationManagerEnabled(*d->m_helpEngine, + enabled); +} + +const QByteArray HelpEngineWrapper::aboutMenuTexts() const +{ + TRACE_OBJ + return CollectionConfiguration::aboutMenuTexts(*d->m_helpEngine); +} + +void HelpEngineWrapper::setAboutMenuTexts(const QByteArray &texts) +{ + TRACE_OBJ + CollectionConfiguration::setAboutMenuTexts(*d->m_helpEngine, texts); +} + +const QByteArray HelpEngineWrapper::aboutIcon() const +{ + TRACE_OBJ + return CollectionConfiguration::aboutIcon(*d->m_helpEngine); +} + +void HelpEngineWrapper::setAboutIcon(const QByteArray &icon) +{ + TRACE_OBJ + CollectionConfiguration::setAboutIcon(*d->m_helpEngine, icon); +} + +const QByteArray HelpEngineWrapper::aboutImages() const +{ + TRACE_OBJ + return CollectionConfiguration::aboutImages(*d->m_helpEngine); +} + +void HelpEngineWrapper::setAboutImages(const QByteArray &images) +{ + TRACE_OBJ + CollectionConfiguration::setAboutImages(*d->m_helpEngine, images); +} + +const QByteArray HelpEngineWrapper::aboutTexts() const +{ + TRACE_OBJ + return CollectionConfiguration::aboutTexts(*d->m_helpEngine); +} + +void HelpEngineWrapper::setAboutTexts(const QByteArray &texts) +{ + TRACE_OBJ + CollectionConfiguration::setAboutTexts(*d->m_helpEngine, texts); +} + +const QString HelpEngineWrapper::windowTitle() const +{ + TRACE_OBJ + return CollectionConfiguration::windowTitle(*d->m_helpEngine); +} + +void HelpEngineWrapper::setWindowTitle(const QString &windowTitle) +{ + TRACE_OBJ + CollectionConfiguration::setWindowTitle(*d->m_helpEngine, windowTitle); +} + +const QByteArray HelpEngineWrapper::applicationIcon() const +{ + TRACE_OBJ + return CollectionConfiguration::applicationIcon(*d->m_helpEngine); +} + +void HelpEngineWrapper::setApplicationIcon(const QByteArray &icon) +{ + TRACE_OBJ + CollectionConfiguration::setApplicationIcon(*d->m_helpEngine, icon); +} + +const QByteArray HelpEngineWrapper::mainWindow() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(MainWindowKey).toByteArray(); +} + +void HelpEngineWrapper::setMainWindow(const QByteArray &mainWindow) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(MainWindowKey, mainWindow); +} + +const QByteArray HelpEngineWrapper::mainWindowGeometry() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(MainWindowGeometryKey).toByteArray(); +} + +void HelpEngineWrapper::setMainWindowGeometry(const QByteArray &geometry) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(MainWindowGeometryKey, geometry); +} + +const QByteArray HelpEngineWrapper::bookmarks() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(BookmarksKey).toByteArray(); +} + +void HelpEngineWrapper::setBookmarks(const QByteArray &bookmarks) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(BookmarksKey, bookmarks); +} + +int HelpEngineWrapper::lastTabPage() const +{ + TRACE_OBJ + return CollectionConfiguration::lastTabPage(*d->m_helpEngine); +} + +void HelpEngineWrapper::setLastTabPage(int lastPage) +{ + TRACE_OBJ + CollectionConfiguration::setLastTabPage(*d->m_helpEngine, lastPage); +} + +int HelpEngineWrapper::startOption() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(StartOptionKey, ShowLastPages).toInt(); +} + +void HelpEngineWrapper::setStartOption(int option) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(StartOptionKey, option); +} + +const QString HelpEngineWrapper::homePage() const +{ + TRACE_OBJ + const QString &homePage + = d->m_helpEngine->customValue(HomePageKey).toString(); + if (!homePage.isEmpty()) + return homePage; + return defaultHomePage(); +} + +void HelpEngineWrapper::setHomePage(const QString &page) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(HomePageKey, page); + +} + +const QString HelpEngineWrapper::defaultHomePage() const +{ + TRACE_OBJ + return CollectionConfiguration::defaultHomePage(*d->m_helpEngine); +} + +void HelpEngineWrapper::setDefaultHomePage(const QString &page) +{ + TRACE_OBJ + CollectionConfiguration::setDefaultHomePage(*d->m_helpEngine, page); +} + +bool HelpEngineWrapper::hasFontSettings() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(UseAppFontKey).isValid(); +} + +bool HelpEngineWrapper::usesAppFont() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(UseAppFontKey).toBool(); +} + +void HelpEngineWrapper::setUseAppFont(bool useAppFont) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(UseAppFontKey, useAppFont); +} + +bool HelpEngineWrapper::usesBrowserFont() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(UseBrowserFontKey, false).toBool(); +} + +void HelpEngineWrapper::setUseBrowserFont(bool useBrowserFont) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(UseBrowserFontKey, useBrowserFont); +} + +const QFont HelpEngineWrapper::appFont() const +{ + TRACE_OBJ + return qvariant_cast<QFont>(d->m_helpEngine->customValue(AppFontKey)); +} + +void HelpEngineWrapper::setAppFont(const QFont &font) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(AppFontKey, font); +} + +QFontDatabase::WritingSystem HelpEngineWrapper::appWritingSystem() const +{ + TRACE_OBJ + return static_cast<QFontDatabase::WritingSystem>( + d->m_helpEngine->customValue(AppWritingSystemKey).toInt()); +} + +void HelpEngineWrapper::setAppWritingSystem(QFontDatabase::WritingSystem system) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(AppWritingSystemKey, system); +} + +const QFont HelpEngineWrapper::browserFont() const +{ + TRACE_OBJ + return qvariant_cast<QFont>(d->m_helpEngine->customValue(BrowserFontKey)); +} + +void HelpEngineWrapper::setBrowserFont(const QFont &font) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(BrowserFontKey, font); +} + +QFontDatabase::WritingSystem HelpEngineWrapper::browserWritingSystem() const +{ + TRACE_OBJ + return static_cast<QFontDatabase::WritingSystem>( + d->m_helpEngine->customValue(BrowserWritingSystemKey).toInt()); +} + +void HelpEngineWrapper::setBrowserWritingSystem(QFontDatabase::WritingSystem system) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(BrowserWritingSystemKey, system); +} + +void HelpEngineWrapper::handleCurrentFilterChanged(const QString &filter) +{ + TRACE_OBJ + const QString &filterToReport + = filter == Unfiltered ? TrUnfiltered : filter; + emit currentFilterChanged(filterToReport); +} + +bool HelpEngineWrapper::showTabs() const +{ + TRACE_OBJ + return d->m_helpEngine->customValue(ShowTabsKey, false).toBool(); +} + +void HelpEngineWrapper::setShowTabs(bool show) +{ + TRACE_OBJ + d->m_helpEngine->setCustomValue(ShowTabsKey, show); +} + +bool HelpEngineWrapper::fullTextSearchFallbackEnabled() const +{ + TRACE_OBJ + return CollectionConfiguration::fullTextSearchFallbackEnabled(*d->m_helpEngine); +} + +// -- TimeoutForwarder + +TimeoutForwarder::TimeoutForwarder(const QString &fileName) + : m_fileName(fileName) +{ + TRACE_OBJ +} + +void TimeoutForwarder::forward() +{ + TRACE_OBJ + HelpEngineWrapper::instance().d->qchFileChanged(m_fileName, true); +} + +// -- HelpEngineWrapperPrivate + +HelpEngineWrapperPrivate::HelpEngineWrapperPrivate(const QString &collectionFile) + : m_helpEngine(new QHelpEngine(collectionFile, this)), + m_qchWatcher(new QFileSystemWatcher(this)) +{ + TRACE_OBJ + if (!m_helpEngine->customFilters().contains(Unfiltered)) + m_helpEngine->addCustomFilter(Unfiltered, QStringList()); + initFileSystemWatchers(); +} + +void HelpEngineWrapperPrivate::initFileSystemWatchers() +{ + TRACE_OBJ + foreach(const QString &ns, m_helpEngine->registeredDocumentations()) { + const QString &docFile = m_helpEngine->documentationFileName(ns); + m_qchWatcher->addPath(docFile); + connect(m_qchWatcher, SIGNAL(fileChanged(QString)), + this, SLOT(qchFileChanged(QString))); + } + checkDocFilesWatched(); +} + +void HelpEngineWrapperPrivate::qchFileChanged(const QString &fileName) +{ + TRACE_OBJ + qchFileChanged(fileName, false); +} + +void HelpEngineWrapperPrivate::checkDocFilesWatched() +{ + TRACE_OBJ + const int watchedFilesCount = m_qchWatcher->files().count(); + const int docFilesCount = m_helpEngine->registeredDocumentations().count(); + if (watchedFilesCount != docFilesCount) { + qWarning("Strange: Have %d docs, but %d are being watched", + watchedFilesCount, docFilesCount); + } +} + +void HelpEngineWrapperPrivate::qchFileChanged(const QString &fileName, + bool fromTimeout) +{ + TRACE_OBJ + + /* + * We don't use QHelpEngineCore::namespaceName(fileName), because the file + * may not exist anymore or contain a different namespace. + */ + QString ns; + foreach (const QString &curNs, m_helpEngine->registeredDocumentations()) { + if (m_helpEngine->documentationFileName(curNs) == fileName) { + ns = curNs; + break; + } + } + + /* + * We can't do an assertion here, because QFileSystemWatcher may send the + * signal more than once. + */ + if (ns.isEmpty()) { + m_recentQchUpdates.remove(fileName); + return; + } + + /* + * Since the QFileSystemWatcher typically sends the signal more than once, + * we repeatedly delay our reaction a bit until we think the last signal + * was sent. + */ + + QMap<QString, RecentSignal>::Iterator it = m_recentQchUpdates.find(fileName); + const QDateTime &now = QDateTime::currentDateTime(); + + // Case 1: This is the first recent signal for the file. + if (it == m_recentQchUpdates.end()) { + QSharedPointer<TimeoutForwarder> forwarder(new TimeoutForwarder(fileName)); + m_recentQchUpdates.insert(fileName, RecentSignal(now, forwarder)); + QTimer::singleShot(UpdateGracePeriod, forwarder.data(), SLOT(forward())); + return; + } + + // Case 2: The last signal for this file has not expired yet. + if (it.value().first > now.addMSecs(-UpdateGracePeriod)) { + if (!fromTimeout) + it.value().first = now; + else + QTimer::singleShot(UpdateGracePeriod, it.value().second.data(), + SLOT(forward())); + return; + } + + // Case 3: The last signal for this file has expired. + if (m_helpEngine->unregisterDocumentation(ns)) { + if (!QFileInfo(fileName).exists() + || !m_helpEngine->registerDocumentation(fileName)) { + m_qchWatcher->removePath(fileName); + emit documentationRemoved(ns); + } else { + emit documentationUpdated(ns); + } + m_helpEngine->setupData(); + } + m_recentQchUpdates.erase(it); +} + +QT_END_NAMESPACE + +#include "helpenginewrapper.moc" diff --git a/src/assistant/tools/assistant/helpenginewrapper.h b/src/assistant/tools/assistant/helpenginewrapper.h new file mode 100644 index 000000000..ff825b04a --- /dev/null +++ b/src/assistant/tools/assistant/helpenginewrapper.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HELPENGINEWRAPPER_H +#define HELPENGINEWRAPPER_H + +#include <QtCore/QMap> +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QUrl> +#include <QtGui/QFont> +#include <QtGui/QFontDatabase> + +QT_BEGIN_NAMESPACE + +class QFileSystemWatcher; +class QHelpContentModel; +class QHelpContentWidget; +class QHelpIndexModel; +class QHelpIndexWidget; +class QHelpSearchEngine; + +enum { + ShowHomePage = 0, + ShowBlankPage = 1, + ShowLastPages = 2 +}; + +class HelpEngineWrapperPrivate; +class TimeoutForwarder; + +class HelpEngineWrapper : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(HelpEngineWrapper) + friend class TimeoutForwarder; +public: + static HelpEngineWrapper &instance(const QString &collectionFile = QString()); + static void removeInstance(); + + // Forwarded help engine member functions, possibly enriched. + QHelpSearchEngine *searchEngine() const; + QHelpContentModel *contentModel() const; + QHelpIndexModel *indexModel() const; + QHelpContentWidget *contentWidget(); + QHelpIndexWidget *indexWidget(); + bool setupData(); + const QStringList registeredDocumentations() const; + const QString collectionFile() const; + bool registerDocumentation(const QString &docFile); + bool unregisterDocumentation(const QString &namespaceName); + bool addCustomFilter(const QString &filterName, + const QStringList &attributes); + bool removeCustomFilter(const QString &filterName); + void setCurrentFilter(const QString &filterName); + const QString currentFilter() const; + const QStringList customFilters() const; + QUrl findFile(const QUrl &url) const; + QByteArray fileData(const QUrl &url) const; + QMap<QString, QUrl> linksForIdentifier(const QString &id) const; + const QStringList filterAttributes() const; + const QStringList filterAttributes(const QString &filterName) const; + QString error() const; + + /* + * To be called after assistant has finished looking for new documentation. + * This will mainly cause the search index to be updated, if necessary. + */ + void initialDocSetupDone(); + + const QStringList qtDocInfo(const QString &component) const; + void setQtDocInfo(const QString &component, const QStringList &doc); + + const QString homePage() const; + void setHomePage(const QString &page); + const QString defaultHomePage() const; + void setDefaultHomePage(const QString &page); + + int lastTabPage() const; + void setLastTabPage(int lastPage); + + // TODO: Don't allow last pages and zoom factors to be set in isolation + // Perhaps also fill up missing elements automatically or assert. + const QStringList lastShownPages() const; + void setLastShownPages(const QStringList &lastShownPages); + const QStringList lastZoomFactors() const; + void setLastZoomFactors(const QStringList &lastZoomFactors); + + const QString cacheDir() const; + bool cacheDirIsRelativeToCollection() const; + void setCacheDir(const QString &cacheDir, bool relativeToCollection); + + bool filterFunctionalityEnabled() const; + void setFilterFunctionalityEnabled(bool enabled); + + bool filterToolbarVisible() const; + void setFilterToolbarVisible(bool visible); + + bool addressBarEnabled() const; + void setAddressBarEnabled(bool enabled); + + bool addressBarVisible() const; + void setAddressBarVisible(bool visible); + + bool documentationManagerEnabled() const; + void setDocumentationManagerEnabled(bool enabled); + + const QByteArray aboutMenuTexts() const; + void setAboutMenuTexts(const QByteArray &texts); + const QByteArray aboutTexts() const; + void setAboutTexts(const QByteArray &texts); + const QByteArray aboutIcon() const; + void setAboutIcon(const QByteArray &icon); + const QByteArray aboutImages() const; + void setAboutImages(const QByteArray &images); + + const QString windowTitle() const; + void setWindowTitle(const QString &windowTitle); + + const QByteArray applicationIcon() const; + void setApplicationIcon(const QByteArray &icon); + + const QByteArray mainWindow() const; + void setMainWindow(const QByteArray &mainWindow); + const QByteArray mainWindowGeometry() const; + void setMainWindowGeometry(const QByteArray &geometry); + + const QByteArray bookmarks() const; + void setBookmarks(const QByteArray &bookmarks); + + int startOption() const; + void setStartOption(int option); + + bool hasFontSettings() const; + bool usesAppFont() const; + void setUseAppFont(bool useAppFont); + bool usesBrowserFont() const; + void setUseBrowserFont(bool useBrowserFont); + const QFont appFont() const; + void setAppFont(const QFont &font); + QFontDatabase::WritingSystem appWritingSystem() const; + void setAppWritingSystem(QFontDatabase::WritingSystem system); + const QFont browserFont() const; + void setBrowserFont(const QFont &font); + QFontDatabase::WritingSystem browserWritingSystem() const; + void setBrowserWritingSystem(QFontDatabase::WritingSystem system); + + bool showTabs() const; + void setShowTabs(bool show); + + static const QString TrUnfiltered; + + bool fullTextSearchFallbackEnabled() const; + +signals: + + // For asynchronous doc updates triggered by external actions. + void documentationRemoved(const QString &namespaceName); + void documentationUpdated(const QString &namespaceName); + + // Forwarded from QHelpEngineCore. + void currentFilterChanged(const QString ¤tFilter); + void setupFinished(); + +private slots: + void handleCurrentFilterChanged(const QString &filter); + +private: + HelpEngineWrapper(const QString &collectionFile); + ~HelpEngineWrapper(); + + static HelpEngineWrapper *helpEngineWrapper; + + HelpEngineWrapperPrivate *d; +}; + +QT_END_NAMESPACE + +#endif // HELPENGINEWRAPPER_H diff --git a/src/assistant/tools/assistant/helpviewer.cpp b/src/assistant/tools/assistant/helpviewer.cpp new file mode 100644 index 000000000..7bed4965a --- /dev/null +++ b/src/assistant/tools/assistant/helpviewer.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "helpviewer.h" +#include "helpviewer_p.h" + +#include "helpenginewrapper.h" +#include "tracer.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QFileInfo> +#include <QtCore/QStringBuilder> +#include <QtCore/QTemporaryFile> +#include <QtCore/QUrl> + +#include <QtGui/QDesktopServices> +#include <QtGui/QMouseEvent> + +#include <QtHelp/QHelpEngineCore> + +QT_BEGIN_NAMESPACE + +const QString HelpViewer::DocPath = QLatin1String("qthelp://com.trolltech."); + +const QString HelpViewer::AboutBlank = + QCoreApplication::translate("HelpViewer", "<title>about:blank</title>"); + +const QString HelpViewer::LocalHelpFile = QLatin1String("qthelp://" + "com.trolltech.com.assistantinternal-1.0.0/assistant/assistant.html"); + +const QString HelpViewer::PageNotFoundMessage = + QCoreApplication::translate("HelpViewer", "<title>Error 404...</title><div " + "align=\"center\"><br><br><h1>The page could not be found</h1><br><h3>'%1'" + "</h3></div>"); + +struct ExtensionMap { + const char *extension; + const char *mimeType; +} extensionMap[] = { + { ".bmp", "image/bmp" }, + { ".css", "text/css" }, + { ".gif", "image/gif" }, + { ".html", "text/html" }, + { ".htm", "text/html" }, + { ".ico", "image/x-icon" }, + { ".jpeg", "image/jpeg" }, + { ".jpg", "image/jpeg" }, + { ".js", "application/x-javascript" }, + { ".mng", "video/x-mng" }, + { ".pbm", "image/x-portable-bitmap" }, + { ".pgm", "image/x-portable-graymap" }, + { ".pdf", "application/pdf" }, + { ".png", "image/png" }, + { ".ppm", "image/x-portable-pixmap" }, + { ".rss", "application/rss+xml" }, + { ".svg", "image/svg+xml" }, + { ".svgz", "image/svg+xml" }, + { ".text", "text/plain" }, + { ".tif", "image/tiff" }, + { ".tiff", "image/tiff" }, + { ".txt", "text/plain" }, + { ".xbm", "image/x-xbitmap" }, + { ".xml", "text/xml" }, + { ".xpm", "image/x-xpm" }, + { ".xsl", "text/xsl" }, + { ".xhtml", "application/xhtml+xml" }, + { ".wml", "text/vnd.wap.wml" }, + { ".wmlc", "application/vnd.wap.wmlc" }, + { "about:blank", 0 }, + { 0, 0 } +}; + +HelpViewer::~HelpViewer() +{ + TRACE_OBJ + delete d; +} + +bool HelpViewer::isLocalUrl(const QUrl &url) +{ + TRACE_OBJ + const QString &scheme = url.scheme(); + return scheme.isEmpty() + || scheme == QLatin1String("file") + || scheme == QLatin1String("qrc") + || scheme == QLatin1String("data") + || scheme == QLatin1String("qthelp") + || scheme == QLatin1String("about"); +} + +bool HelpViewer::canOpenPage(const QString &url) +{ + TRACE_OBJ + return !mimeFromUrl(url).isEmpty(); +} + +QString HelpViewer::mimeFromUrl(const QUrl &url) +{ + TRACE_OBJ + const QString &path = url.path(); + const int index = path.lastIndexOf(QLatin1Char('.')); + const QByteArray &ext = path.mid(index).toUtf8().toLower(); + + const ExtensionMap *e = extensionMap; + while (e->extension) { + if (ext == e->extension) + return QLatin1String(e->mimeType); + ++e; + } + return QLatin1String(""); +} + +bool HelpViewer::launchWithExternalApp(const QUrl &url) +{ + TRACE_OBJ + if (isLocalUrl(url)) { + const HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + const QUrl &resolvedUrl = helpEngine.findFile(url); + if (!resolvedUrl.isValid()) + return false; + + const QString& path = resolvedUrl.path(); + if (!canOpenPage(path)) { + QTemporaryFile tmpTmpFile; + if (!tmpTmpFile.open()) + return false; + + const QString &extension = QFileInfo(path).completeSuffix(); + QFile actualTmpFile(tmpTmpFile.fileName() % QLatin1String(".") + % extension); + if (!actualTmpFile.open(QIODevice::ReadWrite | QIODevice::Truncate)) + return false; + + actualTmpFile.write(helpEngine.fileData(resolvedUrl)); + actualTmpFile.close(); + return QDesktopServices::openUrl(QUrl(actualTmpFile.fileName())); + } + } else if (url.scheme() == QLatin1String("http")) { + return QDesktopServices::openUrl(url); + } + return false; +} + +// -- public slots + +void HelpViewer::home() +{ + TRACE_OBJ + setSource(HelpEngineWrapper::instance().homePage()); +} + +// -- private slots + +void HelpViewer::setLoadStarted() +{ + d->m_loadFinished = false; +} + +void HelpViewer::setLoadFinished(bool ok) +{ + d->m_loadFinished = ok; + emit sourceChanged(source()); +} + +// -- private + +bool HelpViewer::handleForwardBackwardMouseButtons(QMouseEvent *event) +{ + TRACE_OBJ + if (event->button() == Qt::XButton1) { + backward(); + return true; + } + + if (event->button() == Qt::XButton2) { + forward(); + return true; + } + + return false; +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/helpviewer.h b/src/assistant/tools/assistant/helpviewer.h new file mode 100644 index 000000000..bba01d0e6 --- /dev/null +++ b/src/assistant/tools/assistant/helpviewer.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HELPVIEWER_H +#define HELPVIEWER_H + +#include <QtCore/qglobal.h> +#include <QtCore/QString> +#include <QtCore/QUrl> +#include <QtCore/QVariant> + +#include <QtGui/QAction> +#include <QtGui/QFont> + +#if defined(QT_NO_WEBKIT) +#include <QtGui/QTextBrowser> +#else +#include <QtWebKit/QWebView> +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(QT_NO_WEBKIT) +class HelpViewer : public QWebView +#else +class HelpViewer : public QTextBrowser +#endif +{ + Q_OBJECT + class HelpViewerPrivate; + Q_DISABLE_COPY(HelpViewer) + +public: + enum FindFlag { + FindBackward = 0x01, + FindCaseSensitively = 0x02 + }; + Q_DECLARE_FLAGS(FindFlags, FindFlag) + + HelpViewer(qreal zoom, QWidget *parent = 0); + ~HelpViewer(); + + QFont viewerFont() const; + void setViewerFont(const QFont &font); + + void scaleUp(); + void scaleDown(); + + void resetScale(); + qreal scale() const; + + QString title() const; + void setTitle(const QString &title); + + QUrl source() const; + void setSource(const QUrl &url); + + QString selectedText() const; + bool isForwardAvailable() const; + bool isBackwardAvailable() const; + + bool findText(const QString &text, FindFlags flags, bool incremental, + bool fromSearch); + + static const QString DocPath; + static const QString AboutBlank; + static const QString LocalHelpFile; + static const QString PageNotFoundMessage; + + static bool isLocalUrl(const QUrl &url); + static bool canOpenPage(const QString &url); + static QString mimeFromUrl(const QUrl &url); + static bool launchWithExternalApp(const QUrl &url); + +public slots: + void copy(); + void home(); + + void forward(); + void backward(); + +signals: + void titleChanged(); +#if !defined(QT_NO_WEBKIT) + void copyAvailable(bool yes); + void sourceChanged(const QUrl &url); + void forwardAvailable(bool enabled); + void backwardAvailable(bool enabled); + void highlighted(const QString &link); + void printRequested(); +#else + void loadStarted(); + void loadFinished(bool finished); +#endif + +protected: + void keyPressEvent(QKeyEvent *e); + void wheelEvent(QWheelEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + +private slots: + void actionChanged(); + void setLoadStarted(); + void setLoadFinished(bool ok); + +private: + bool eventFilter(QObject *obj, QEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + QVariant loadResource(int type, const QUrl &name); + bool handleForwardBackwardMouseButtons(QMouseEvent *e); + +private: + HelpViewerPrivate *d; +}; + +QT_END_NAMESPACE +Q_DECLARE_METATYPE(HelpViewer*) + +#endif // HELPVIEWER_H diff --git a/src/assistant/tools/assistant/helpviewer_p.h b/src/assistant/tools/assistant/helpviewer_p.h new file mode 100644 index 000000000..378871197 --- /dev/null +++ b/src/assistant/tools/assistant/helpviewer_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HELPVIEWERPRIVATE_H +#define HELPVIEWERPRIVATE_H + +#include "centralwidget.h" +#include "helpviewer.h" +#include "openpagesmanager.h" + +#include <QtCore/QObject> +#ifdef QT_NO_WEBKIT +#include <QtGui/QTextBrowser> +#endif + +QT_BEGIN_NAMESPACE + +class HelpViewer::HelpViewerPrivate : public QObject +{ + Q_OBJECT + +public: +#ifdef QT_NO_WEBKIT + HelpViewerPrivate(int zoom) + : zoomCount(zoom) + , forceFont(false) + , lastAnchor(QString()) +#else + HelpViewerPrivate() +#endif + { + m_loadFinished = false; + } + +#ifdef QT_NO_WEBKIT + bool hasAnchorAt(QTextBrowser *browser, const QPoint& pos) + { + lastAnchor = browser->anchorAt(pos); + if (lastAnchor.isEmpty()) + return false; + + lastAnchor = browser->source().resolved(lastAnchor).toString(); + if (lastAnchor.at(0) == QLatin1Char('#')) { + QString src = browser->source().toString(); + int hsh = src.indexOf(QLatin1Char('#')); + lastAnchor = (hsh >= 0 ? src.left(hsh) : src) + lastAnchor; + } + return true; + } + + void openLink(bool newPage) + { + if(lastAnchor.isEmpty()) + return; + if (newPage) + OpenPagesManager::instance()->createPage(lastAnchor); + else + CentralWidget::instance()->setSource(lastAnchor); + lastAnchor.clear(); + } + +public slots: + void openLink() + { + openLink(false); + } + + void openLinkInNewPage() + { + openLink(true); + } + +public: + int zoomCount; + bool forceFont; + QString lastAnchor; +#endif // QT_NO_WEBKIT + +public: + bool m_loadFinished; +}; + +QT_END_NAMESPACE + +#endif // HELPVIEWERPRIVATE_H diff --git a/src/assistant/tools/assistant/helpviewer_qtb.cpp b/src/assistant/tools/assistant/helpviewer_qtb.cpp new file mode 100644 index 000000000..0a6325cb5 --- /dev/null +++ b/src/assistant/tools/assistant/helpviewer_qtb.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "helpviewer.h" + +#include "globalactions.h" +#include "helpenginewrapper.h" +#include "helpviewer_p.h" +#include "openpagesmanager.h" +#include "tracer.h" + +#include <QtCore/QStringBuilder> + +#include <QtGui/QContextMenuEvent> +#include <QtGui/QMenu> +#include <QtGui/QClipboard> +#include <QtGui/QApplication> + +QT_BEGIN_NAMESPACE + +HelpViewer::HelpViewer(qreal zoom, QWidget *parent) + : QTextBrowser(parent) + , d(new HelpViewerPrivate(zoom)) +{ + TRACE_OBJ + QPalette p = palette(); + p.setColor(QPalette::Inactive, QPalette::Highlight, + p.color(QPalette::Active, QPalette::Highlight)); + p.setColor(QPalette::Inactive, QPalette::HighlightedText, + p.color(QPalette::Active, QPalette::HighlightedText)); + setPalette(p); + + installEventFilter(this); + document()->setDocumentMargin(8); + + QFont font = viewerFont(); + font.setPointSize(int(font.pointSize() + zoom)); + setViewerFont(font); + + connect(this, SIGNAL(sourceChanged(QUrl)), this, SIGNAL(titleChanged())); + connect(this, SIGNAL(loadFinished(bool)), this, SLOT(setLoadFinished(bool))); +} + +QFont HelpViewer::viewerFont() const +{ + TRACE_OBJ + if (HelpEngineWrapper::instance().usesBrowserFont()) + return HelpEngineWrapper::instance().browserFont(); + return qApp->font(); +} + +void HelpViewer::setViewerFont(const QFont &newFont) +{ + TRACE_OBJ + if (font() != newFont) { + d->forceFont = true; + setFont(newFont); + d->forceFont = false; + } +} + +void HelpViewer::scaleUp() +{ + TRACE_OBJ + if (d->zoomCount < 10) { + d->zoomCount++; + d->forceFont = true; + zoomIn(); + d->forceFont = false; + } +} + +void HelpViewer::scaleDown() +{ + TRACE_OBJ + if (d->zoomCount > -5) { + d->zoomCount--; + d->forceFont = true; + zoomOut(); + d->forceFont = false; + } +} + +void HelpViewer::resetScale() +{ + TRACE_OBJ + if (d->zoomCount != 0) { + d->forceFont = true; + zoomOut(d->zoomCount); + d->forceFont = false; + } + d->zoomCount = 0; +} + +qreal HelpViewer::scale() const +{ + TRACE_OBJ + return d->zoomCount; +} + +QString HelpViewer::title() const +{ + TRACE_OBJ + return documentTitle(); +} + +void HelpViewer::setTitle(const QString &title) +{ + TRACE_OBJ + setDocumentTitle(title); +} + +QUrl HelpViewer::source() const +{ + TRACE_OBJ + return QTextBrowser::source(); +} + +void HelpViewer::setSource(const QUrl &url) +{ + TRACE_OBJ + if (launchWithExternalApp(url)) + return; + + emit loadStarted(); + QString string = url.toString(); + const HelpEngineWrapper &engine = HelpEngineWrapper::instance(); + const QUrl &resolvedUrl = (string == QLatin1String("help") ? LocalHelpFile : + engine.findFile(string)); + QTextBrowser::setSource(resolvedUrl); + if (!url.isValid()) { + setHtml(string == QLatin1String("about:blank") ? AboutBlank + : PageNotFoundMessage.arg(url.toString())); + } + emit loadFinished(true); +} + +QString HelpViewer::selectedText() const +{ + TRACE_OBJ + return textCursor().selectedText(); +} + +bool HelpViewer::isForwardAvailable() const +{ + TRACE_OBJ + return QTextBrowser::isForwardAvailable(); +} + +bool HelpViewer::isBackwardAvailable() const +{ + TRACE_OBJ + return QTextBrowser::isBackwardAvailable(); +} + +bool HelpViewer::findText(const QString &text, FindFlags flags, bool incremental, + bool fromSearch) +{ + TRACE_OBJ + QTextDocument *doc = document(); + QTextCursor cursor = textCursor(); + if (!doc || cursor.isNull()) + return false; + + const int position = cursor.selectionStart(); + if (incremental) + cursor.setPosition(position); + + QTextDocument::FindFlags textDocFlags; + if (flags & HelpViewer::FindBackward) + textDocFlags |= QTextDocument::FindBackward; + if (flags & HelpViewer::FindCaseSensitively) + textDocFlags |= QTextDocument::FindCaseSensitively; + + QTextCursor found = doc->find(text, cursor, textDocFlags); + if (found.isNull()) { + if ((flags & HelpViewer::FindBackward) == 0) + cursor.movePosition(QTextCursor::Start); + else + cursor.movePosition(QTextCursor::End); + found = doc->find(text, cursor, textDocFlags); + } + + if (fromSearch) { + cursor.beginEditBlock(); + viewport()->setUpdatesEnabled(false); + + QTextCharFormat marker; + marker.setForeground(Qt::red); + cursor.movePosition(QTextCursor::Start); + setTextCursor(cursor); + + while (find(text)) { + QTextCursor hit = textCursor(); + hit.mergeCharFormat(marker); + } + + viewport()->setUpdatesEnabled(true); + cursor.endEditBlock(); + } + + bool cursorIsNull = found.isNull(); + if (cursorIsNull) { + found = textCursor(); + found.setPosition(position); + } + setTextCursor(found); + return !cursorIsNull; +} + +// -- public slots + +void HelpViewer::copy() +{ + TRACE_OBJ + QTextBrowser::copy(); +} + +void HelpViewer::forward() +{ + TRACE_OBJ + QTextBrowser::forward(); +} + +void HelpViewer::backward() +{ + TRACE_OBJ + QTextBrowser::backward(); +} + +// -- protected + +void HelpViewer::keyPressEvent(QKeyEvent *e) +{ + TRACE_OBJ + if ((e->key() == Qt::Key_Home && e->modifiers() != Qt::NoModifier) + || (e->key() == Qt::Key_End && e->modifiers() != Qt::NoModifier)) { + QKeyEvent* event = new QKeyEvent(e->type(), e->key(), Qt::NoModifier, + e->text(), e->isAutoRepeat(), e->count()); + e = event; + } + QTextBrowser::keyPressEvent(e); +} + + +void HelpViewer::wheelEvent(QWheelEvent *e) +{ + TRACE_OBJ + if (e->modifiers() == Qt::ControlModifier) { + e->accept(); + e->delta() > 0 ? scaleUp() : scaleDown(); + } else { + QTextBrowser::wheelEvent(e); + } +} + +void HelpViewer::mousePressEvent(QMouseEvent *e) +{ + TRACE_OBJ +#ifdef Q_OS_LINUX + if (handleForwardBackwardMouseButtons(e)) + return; +#endif + + QTextBrowser::mousePressEvent(e); +} + +void HelpViewer::mouseReleaseEvent(QMouseEvent *e) +{ + TRACE_OBJ +#ifndef Q_OS_LINUX + if (handleForwardBackwardMouseButtons(e)) + return; +#endif + + bool controlPressed = e->modifiers() & Qt::ControlModifier; + if ((controlPressed && d->hasAnchorAt(this, e->pos())) || + (e->button() == Qt::MidButton && d->hasAnchorAt(this, e->pos()))) { + d->openLinkInNewPage(); + return; + } + + QTextBrowser::mouseReleaseEvent(e); +} + +// -- private slots + +void HelpViewer::actionChanged() +{ + // stub + TRACE_OBJ +} + +// -- private + +bool HelpViewer::eventFilter(QObject *obj, QEvent *event) +{ + TRACE_OBJ + if (event->type() == QEvent::FontChange && !d->forceFont) + return true; + return QTextBrowser::eventFilter(obj, event); +} + +void HelpViewer::contextMenuEvent(QContextMenuEvent *event) +{ + TRACE_OBJ + + QMenu menu(QString(), 0); + QUrl link; + QAction *copyAnchorAction = 0; + if (d->hasAnchorAt(this, event->pos())) { + link = anchorAt(event->pos()); + if (link.isRelative()) + link = source().resolved(link); + menu.addAction(tr("Open Link"), d, SLOT(openLink())); + menu.addAction(tr("Open Link in New Tab\tCtrl+LMB"), d, SLOT(openLinkInNewPage())); + + if (!link.isEmpty() && link.isValid()) + copyAnchorAction = menu.addAction(tr("Copy &Link Location")); + } else if (!selectedText().isEmpty()) { + menu.addAction(tr("Copy"), this, SLOT(copy())); + } else { + menu.addAction(tr("Reload"), this, SLOT(reload())); + } + + if (copyAnchorAction == menu.exec(event->globalPos())) + QApplication::clipboard()->setText(link.toString()); +} + +QVariant HelpViewer::loadResource(int type, const QUrl &name) +{ + TRACE_OBJ + QByteArray ba; + if (type < 4) { + ba = HelpEngineWrapper::instance().fileData(name); + if (name.toString().endsWith(QLatin1String(".svg"), Qt::CaseInsensitive)) { + QImage image; + image.loadFromData(ba, "svg"); + if (!image.isNull()) + return image; + } + } + return ba; +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/helpviewer_qwv.cpp b/src/assistant/tools/assistant/helpviewer_qwv.cpp new file mode 100644 index 000000000..efb9b5900 --- /dev/null +++ b/src/assistant/tools/assistant/helpviewer_qwv.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "helpviewer.h" +#include "helpviewer_p.h" + +#include "centralwidget.h" +#include "helpenginewrapper.h" +#include "openpagesmanager.h" +#include "tracer.h" + +#include <QtCore/QFileInfo> +#include <QtCore/QString> +#include <QtCore/QTimer> + +#include <QtGui/QApplication> +#include <QtGui/QWheelEvent> + +#include <QtHelp/QHelpEngineCore> + +#include <QtNetwork/QNetworkAccessManager> +#include <QtNetwork/QNetworkReply> +#include <QtNetwork/QNetworkRequest> + +QT_BEGIN_NAMESPACE + +// -- HelpNetworkReply + +class HelpNetworkReply : public QNetworkReply +{ +public: + HelpNetworkReply(const QNetworkRequest &request, const QByteArray &fileData, + const QString &mimeType); + + virtual void abort(); + + virtual qint64 bytesAvailable() const + { return data.length() + QNetworkReply::bytesAvailable(); } + +protected: + virtual qint64 readData(char *data, qint64 maxlen); + +private: + QByteArray data; + qint64 origLen; +}; + +HelpNetworkReply::HelpNetworkReply(const QNetworkRequest &request, + const QByteArray &fileData, const QString& mimeType) + : data(fileData), origLen(fileData.length()) +{ + TRACE_OBJ + setRequest(request); + setOpenMode(QIODevice::ReadOnly); + + setHeader(QNetworkRequest::ContentTypeHeader, mimeType); + setHeader(QNetworkRequest::ContentLengthHeader, QByteArray::number(origLen)); + QTimer::singleShot(0, this, SIGNAL(metaDataChanged())); + QTimer::singleShot(0, this, SIGNAL(readyRead())); +} + +void HelpNetworkReply::abort() +{ + TRACE_OBJ +} + +qint64 HelpNetworkReply::readData(char *buffer, qint64 maxlen) +{ + TRACE_OBJ + qint64 len = qMin(qint64(data.length()), maxlen); + if (len) { + memcpy(buffer, data.constData(), len); + data.remove(0, len); + } + if (!data.length()) + QTimer::singleShot(0, this, SIGNAL(finished())); + return len; +} + +// -- HelpNetworkAccessManager + +class HelpNetworkAccessManager : public QNetworkAccessManager +{ +public: + HelpNetworkAccessManager(QObject *parent); + +protected: + virtual QNetworkReply *createRequest(Operation op, + const QNetworkRequest &request, QIODevice *outgoingData = 0); +}; + +HelpNetworkAccessManager::HelpNetworkAccessManager(QObject *parent) + : QNetworkAccessManager(parent) +{ + TRACE_OBJ +} + +QNetworkReply *HelpNetworkAccessManager::createRequest(Operation /*op*/, + const QNetworkRequest &request, QIODevice* /*outgoingData*/) +{ + TRACE_OBJ + QString url = request.url().toString(); + const HelpEngineWrapper &engine = HelpEngineWrapper::instance(); + // TODO: For some reason the url to load is already wrong (passed from webkit) + // though the css file and the references inside should work that way. One + // possible problem might be that the css is loaded at the same level as the + // html, thus a path inside the css like (../images/foo.png) might cd out of + // the virtual folder + if (!engine.findFile(url).isValid()) { + if (url.startsWith(HelpViewer::DocPath)) { + QUrl newUrl = request.url(); + if (!newUrl.path().startsWith(QLatin1String("/qdoc/"))) { + newUrl.setPath(QLatin1String("qdoc") + newUrl.path()); + url = newUrl.toString(); + } + } + } + + const QString &mimeType = HelpViewer::mimeFromUrl(url); + const QByteArray &data = engine.findFile(url).isValid() ? engine.fileData(url) + : HelpViewer::PageNotFoundMessage.arg(url).toUtf8(); + + return new HelpNetworkReply(request, data, mimeType.isEmpty() + ? QLatin1String("application/octet-stream") : mimeType); +} + +// -- HelpPage + +class HelpPage : public QWebPage +{ +public: + HelpPage(QObject *parent); + +protected: + virtual QWebPage *createWindow(QWebPage::WebWindowType); + virtual void triggerAction(WebAction action, bool checked = false); + + virtual bool acceptNavigationRequest(QWebFrame *frame, + const QNetworkRequest &request, NavigationType type); + +private: + bool closeNewTabIfNeeded; + + friend class HelpViewer; + QUrl m_loadingUrl; + Qt::MouseButtons m_pressedButtons; + Qt::KeyboardModifiers m_keyboardModifiers; +}; + +HelpPage::HelpPage(QObject *parent) + : QWebPage(parent) + , closeNewTabIfNeeded(false) + , m_pressedButtons(Qt::NoButton) + , m_keyboardModifiers(Qt::NoModifier) +{ + TRACE_OBJ +} + +QWebPage *HelpPage::createWindow(QWebPage::WebWindowType) +{ + TRACE_OBJ + HelpPage* newPage = static_cast<HelpPage*>(OpenPagesManager::instance() + ->createPage()->page()); + newPage->closeNewTabIfNeeded = closeNewTabIfNeeded; + closeNewTabIfNeeded = false; + return newPage; +} + +void HelpPage::triggerAction(WebAction action, bool checked) +{ + TRACE_OBJ + switch (action) { + case OpenLinkInNewWindow: + closeNewTabIfNeeded = true; + default: // fall through + QWebPage::triggerAction(action, checked); + break; + } +} + +bool HelpPage::acceptNavigationRequest(QWebFrame *, + const QNetworkRequest &request, QWebPage::NavigationType type) +{ + TRACE_OBJ + const bool closeNewTab = closeNewTabIfNeeded; + closeNewTabIfNeeded = false; + + const QUrl &url = request.url(); + if (HelpViewer::launchWithExternalApp(url)) { + if (closeNewTab) + QMetaObject::invokeMethod(OpenPagesManager::instance(), "closeCurrentPage"); + return false; + } + + if (type == QWebPage::NavigationTypeLinkClicked + && (m_keyboardModifiers & Qt::ControlModifier || m_pressedButtons == Qt::MidButton)) { + m_pressedButtons = Qt::NoButton; + m_keyboardModifiers = Qt::NoModifier; + OpenPagesManager::instance()->createPage(url); + return false; + } + + m_loadingUrl = url; // because of async page loading, we will hit some kind + // of race condition while using a remote command, like a combination of + // SetSource; SyncContent. SetSource would be called and SyncContents shortly + // afterwards, but the page might not have finished loading and the old url + // would be returned. + return true; +} + +// -- HelpViewer + +HelpViewer::HelpViewer(qreal zoom, QWidget *parent) + : QWebView(parent) + , d(new HelpViewerPrivate) +{ + TRACE_OBJ + setAcceptDrops(false); + settings()->setAttribute(QWebSettings::JavaEnabled, false); + settings()->setAttribute(QWebSettings::PluginsEnabled, false); + + setPage(new HelpPage(this)); + page()->setNetworkAccessManager(new HelpNetworkAccessManager(this)); + + QAction* action = pageAction(QWebPage::OpenLinkInNewWindow); + action->setText(tr("Open Link in New Page")); + + pageAction(QWebPage::DownloadLinkToDisk)->setVisible(false); + pageAction(QWebPage::DownloadImageToDisk)->setVisible(false); + pageAction(QWebPage::OpenImageInNewWindow)->setVisible(false); + + connect(pageAction(QWebPage::Copy), SIGNAL(changed()), this, + SLOT(actionChanged())); + connect(pageAction(QWebPage::Back), SIGNAL(changed()), this, + SLOT(actionChanged())); + connect(pageAction(QWebPage::Forward), SIGNAL(changed()), this, + SLOT(actionChanged())); + connect(page(), SIGNAL(linkHovered(QString, QString, QString)), this, + SIGNAL(highlighted(QString))); + connect(this, SIGNAL(urlChanged(QUrl)), this, SIGNAL(sourceChanged(QUrl))); + connect(this, SIGNAL(loadStarted()), this, SLOT(setLoadStarted())); + connect(this, SIGNAL(loadFinished(bool)), this, SLOT(setLoadFinished(bool))); + connect(this, SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged())); + connect(page(), SIGNAL(printRequested(QWebFrame*)), this, SIGNAL(printRequested())); + + setFont(viewerFont()); + setTextSizeMultiplier(zoom == 0.0 ? 1.0 : zoom); +} + +QFont HelpViewer::viewerFont() const +{ + TRACE_OBJ + if (HelpEngineWrapper::instance().usesBrowserFont()) + return HelpEngineWrapper::instance().browserFont(); + + QWebSettings *webSettings = QWebSettings::globalSettings(); + return QFont(webSettings->fontFamily(QWebSettings::StandardFont), + webSettings->fontSize(QWebSettings::DefaultFontSize)); +} + +void HelpViewer::setViewerFont(const QFont &font) +{ + TRACE_OBJ + QWebSettings *webSettings = settings(); + webSettings->setFontFamily(QWebSettings::StandardFont, font.family()); + webSettings->setFontSize(QWebSettings::DefaultFontSize, font.pointSize()); +} + +void HelpViewer::scaleUp() +{ + TRACE_OBJ + setTextSizeMultiplier(textSizeMultiplier() + 0.1); +} + +void HelpViewer::scaleDown() +{ + TRACE_OBJ + setTextSizeMultiplier(qMax(0.0, textSizeMultiplier() - 0.1)); +} + +void HelpViewer::resetScale() +{ + TRACE_OBJ + setTextSizeMultiplier(1.0); +} + +qreal HelpViewer::scale() const +{ + TRACE_OBJ + return textSizeMultiplier(); +} + +QString HelpViewer::title() const +{ + TRACE_OBJ + return QWebView::title(); +} + +void HelpViewer::setTitle(const QString &title) +{ + TRACE_OBJ + Q_UNUSED(title) +} + +QUrl HelpViewer::source() const +{ + TRACE_OBJ + HelpPage *currentPage = static_cast<HelpPage*> (page()); + if (currentPage && !d->m_loadFinished) { + // see HelpPage::acceptNavigationRequest(...) + return currentPage->m_loadingUrl; + } + return url(); +} + +void HelpViewer::setSource(const QUrl &url) +{ + TRACE_OBJ + load(url.toString() == QLatin1String("help") ? LocalHelpFile : url); +} + +QString HelpViewer::selectedText() const +{ + TRACE_OBJ + return QWebView::selectedText(); +} + +bool HelpViewer::isForwardAvailable() const +{ + TRACE_OBJ + return pageAction(QWebPage::Forward)->isEnabled(); +} + +bool HelpViewer::isBackwardAvailable() const +{ + TRACE_OBJ + return pageAction(QWebPage::Back)->isEnabled(); +} + +bool HelpViewer::findText(const QString &text, FindFlags flags, bool incremental, + bool fromSearch) +{ + TRACE_OBJ + Q_UNUSED(incremental); Q_UNUSED(fromSearch); + QWebPage::FindFlags options = QWebPage::FindWrapsAroundDocument; + if (flags & FindBackward) + options |= QWebPage::FindBackward; + if (flags & FindCaseSensitively) + options |= QWebPage::FindCaseSensitively; + + bool found = QWebView::findText(text, options); + options = QWebPage::HighlightAllOccurrences; + QWebView::findText(QLatin1String(""), options); // clear first + QWebView::findText(text, options); // force highlighting of all other matches + return found; +} + +// -- public slots + +void HelpViewer::copy() +{ + TRACE_OBJ + triggerPageAction(QWebPage::Copy); +} + +void HelpViewer::forward() +{ + TRACE_OBJ + QWebView::forward(); +} + +void HelpViewer::backward() +{ + TRACE_OBJ + back(); +} + +// -- protected + +void HelpViewer::keyPressEvent(QKeyEvent *e) +{ + TRACE_OBJ + // TODO: remove this once we support multiple keysequences per command + if (e->key() == Qt::Key_Insert && e->modifiers() == Qt::CTRL) { + if (!selectedText().isEmpty()) + copy(); + } + QWebView::keyPressEvent(e); +} + +void HelpViewer::wheelEvent(QWheelEvent *event) +{ + TRACE_OBJ + if (event->modifiers()& Qt::ControlModifier) { + event->accept(); + event->delta() > 0 ? scaleUp() : scaleDown(); + } else { + QWebView::wheelEvent(event); + } +} + +void HelpViewer::mousePressEvent(QMouseEvent *event) +{ + TRACE_OBJ +#ifdef Q_OS_LINUX + if (handleForwardBackwardMouseButtons(event)) + return; +#endif + + if (HelpPage *currentPage = static_cast<HelpPage*> (page())) { + currentPage->m_pressedButtons = event->buttons(); + currentPage->m_keyboardModifiers = event->modifiers(); + } + + QWebView::mousePressEvent(event); +} + +void HelpViewer::mouseReleaseEvent(QMouseEvent *event) +{ + TRACE_OBJ +#ifndef Q_OS_LINUX + if (handleForwardBackwardMouseButtons(event)) + return; +#endif + + QWebView::mouseReleaseEvent(event); +} + +// -- private slots + +void HelpViewer::actionChanged() +{ + TRACE_OBJ + QAction *a = qobject_cast<QAction *>(sender()); + if (a == pageAction(QWebPage::Copy)) + emit copyAvailable(a->isEnabled()); + else if (a == pageAction(QWebPage::Back)) + emit backwardAvailable(a->isEnabled()); + else if (a == pageAction(QWebPage::Forward)) + emit forwardAvailable(a->isEnabled()); +} + +// -- private + +bool HelpViewer::eventFilter(QObject *obj, QEvent *event) +{ + TRACE_OBJ + return QWebView::eventFilter(obj, event); +} + +void HelpViewer::contextMenuEvent(QContextMenuEvent *event) +{ + TRACE_OBJ + QWebView::contextMenuEvent(event); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/images/assistant-128.png b/src/assistant/tools/assistant/images/assistant-128.png Binary files differnew file mode 100644 index 000000000..f05949f6d --- /dev/null +++ b/src/assistant/tools/assistant/images/assistant-128.png diff --git a/src/assistant/tools/assistant/images/assistant.png b/src/assistant/tools/assistant/images/assistant.png Binary files differnew file mode 100644 index 000000000..ea4d1e70c --- /dev/null +++ b/src/assistant/tools/assistant/images/assistant.png diff --git a/src/assistant/tools/assistant/images/bookmark.png b/src/assistant/tools/assistant/images/bookmark.png Binary files differnew file mode 100644 index 000000000..57e57e343 --- /dev/null +++ b/src/assistant/tools/assistant/images/bookmark.png diff --git a/src/assistant/tools/assistant/images/closebutton.png b/src/assistant/tools/assistant/images/closebutton.png Binary files differnew file mode 100644 index 000000000..c978cf51a --- /dev/null +++ b/src/assistant/tools/assistant/images/closebutton.png diff --git a/src/assistant/tools/assistant/images/darkclosebutton.png b/src/assistant/tools/assistant/images/darkclosebutton.png Binary files differnew file mode 100644 index 000000000..1077663b2 --- /dev/null +++ b/src/assistant/tools/assistant/images/darkclosebutton.png diff --git a/src/assistant/tools/assistant/images/mac/addtab.png b/src/assistant/tools/assistant/images/mac/addtab.png Binary files differnew file mode 100644 index 000000000..20928fb40 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/addtab.png diff --git a/src/assistant/tools/assistant/images/mac/book.png b/src/assistant/tools/assistant/images/mac/book.png Binary files differnew file mode 100644 index 000000000..7a3204c87 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/book.png diff --git a/src/assistant/tools/assistant/images/mac/closetab.png b/src/assistant/tools/assistant/images/mac/closetab.png Binary files differnew file mode 100644 index 000000000..ab9d669ee --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/closetab.png diff --git a/src/assistant/tools/assistant/images/mac/editcopy.png b/src/assistant/tools/assistant/images/mac/editcopy.png Binary files differnew file mode 100644 index 000000000..f55136446 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/editcopy.png diff --git a/src/assistant/tools/assistant/images/mac/find.png b/src/assistant/tools/assistant/images/mac/find.png Binary files differnew file mode 100644 index 000000000..3561745f0 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/find.png diff --git a/src/assistant/tools/assistant/images/mac/home.png b/src/assistant/tools/assistant/images/mac/home.png Binary files differnew file mode 100644 index 000000000..78d94da18 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/home.png diff --git a/src/assistant/tools/assistant/images/mac/next.png b/src/assistant/tools/assistant/images/mac/next.png Binary files differnew file mode 100644 index 000000000..a585cab80 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/next.png diff --git a/src/assistant/tools/assistant/images/mac/previous.png b/src/assistant/tools/assistant/images/mac/previous.png Binary files differnew file mode 100644 index 000000000..612fb34dc --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/previous.png diff --git a/src/assistant/tools/assistant/images/mac/print.png b/src/assistant/tools/assistant/images/mac/print.png Binary files differnew file mode 100644 index 000000000..10ca56c82 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/print.png diff --git a/src/assistant/tools/assistant/images/mac/resetzoom.png b/src/assistant/tools/assistant/images/mac/resetzoom.png Binary files differnew file mode 100644 index 000000000..759b38296 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/resetzoom.png diff --git a/src/assistant/tools/assistant/images/mac/synctoc.png b/src/assistant/tools/assistant/images/mac/synctoc.png Binary files differnew file mode 100644 index 000000000..067fa941b --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/synctoc.png diff --git a/src/assistant/tools/assistant/images/mac/zoomin.png b/src/assistant/tools/assistant/images/mac/zoomin.png Binary files differnew file mode 100644 index 000000000..d46f5aff0 --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/zoomin.png diff --git a/src/assistant/tools/assistant/images/mac/zoomout.png b/src/assistant/tools/assistant/images/mac/zoomout.png Binary files differnew file mode 100644 index 000000000..46326566d --- /dev/null +++ b/src/assistant/tools/assistant/images/mac/zoomout.png diff --git a/src/assistant/tools/assistant/images/trolltech-logo.png b/src/assistant/tools/assistant/images/trolltech-logo.png Binary files differnew file mode 100644 index 000000000..c53e744ca --- /dev/null +++ b/src/assistant/tools/assistant/images/trolltech-logo.png diff --git a/src/assistant/tools/assistant/images/win/addtab.png b/src/assistant/tools/assistant/images/win/addtab.png Binary files differnew file mode 100644 index 000000000..4bb0feb92 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/addtab.png diff --git a/src/assistant/tools/assistant/images/win/book.png b/src/assistant/tools/assistant/images/win/book.png Binary files differnew file mode 100644 index 000000000..09ec4d33f --- /dev/null +++ b/src/assistant/tools/assistant/images/win/book.png diff --git a/src/assistant/tools/assistant/images/win/closetab.png b/src/assistant/tools/assistant/images/win/closetab.png Binary files differnew file mode 100644 index 000000000..ef9e02086 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/closetab.png diff --git a/src/assistant/tools/assistant/images/win/editcopy.png b/src/assistant/tools/assistant/images/win/editcopy.png Binary files differnew file mode 100644 index 000000000..1121b47d8 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/editcopy.png diff --git a/src/assistant/tools/assistant/images/win/find.png b/src/assistant/tools/assistant/images/win/find.png Binary files differnew file mode 100644 index 000000000..6ea35e930 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/find.png diff --git a/src/assistant/tools/assistant/images/win/home.png b/src/assistant/tools/assistant/images/win/home.png Binary files differnew file mode 100644 index 000000000..b1c6ae191 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/home.png diff --git a/src/assistant/tools/assistant/images/win/next.png b/src/assistant/tools/assistant/images/win/next.png Binary files differnew file mode 100644 index 000000000..8df4127a0 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/next.png diff --git a/src/assistant/tools/assistant/images/win/previous.png b/src/assistant/tools/assistant/images/win/previous.png Binary files differnew file mode 100644 index 000000000..0780bc23d --- /dev/null +++ b/src/assistant/tools/assistant/images/win/previous.png diff --git a/src/assistant/tools/assistant/images/win/print.png b/src/assistant/tools/assistant/images/win/print.png Binary files differnew file mode 100644 index 000000000..ba7c02dc1 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/print.png diff --git a/src/assistant/tools/assistant/images/win/resetzoom.png b/src/assistant/tools/assistant/images/win/resetzoom.png Binary files differnew file mode 100644 index 000000000..b69ae4e7f --- /dev/null +++ b/src/assistant/tools/assistant/images/win/resetzoom.png diff --git a/src/assistant/tools/assistant/images/win/synctoc.png b/src/assistant/tools/assistant/images/win/synctoc.png Binary files differnew file mode 100644 index 000000000..da301bc59 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/synctoc.png diff --git a/src/assistant/tools/assistant/images/win/zoomin.png b/src/assistant/tools/assistant/images/win/zoomin.png Binary files differnew file mode 100644 index 000000000..2e586fc7b --- /dev/null +++ b/src/assistant/tools/assistant/images/win/zoomin.png diff --git a/src/assistant/tools/assistant/images/win/zoomout.png b/src/assistant/tools/assistant/images/win/zoomout.png Binary files differnew file mode 100644 index 000000000..a736d3934 --- /dev/null +++ b/src/assistant/tools/assistant/images/win/zoomout.png diff --git a/src/assistant/tools/assistant/images/wrap.png b/src/assistant/tools/assistant/images/wrap.png Binary files differnew file mode 100644 index 000000000..90f18d9f7 --- /dev/null +++ b/src/assistant/tools/assistant/images/wrap.png diff --git a/src/assistant/tools/assistant/indexwindow.cpp b/src/assistant/tools/assistant/indexwindow.cpp new file mode 100644 index 000000000..38bc8b96a --- /dev/null +++ b/src/assistant/tools/assistant/indexwindow.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "indexwindow.h" + +#include "centralwidget.h" +#include "helpenginewrapper.h" +#include "helpviewer.h" +#include "openpagesmanager.h" +#include "topicchooser.h" +#include "tracer.h" + +#include <QtGui/QLayout> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QKeyEvent> +#include <QtGui/QMenu> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QListWidgetItem> + +#include <QtHelp/QHelpIndexWidget> + +QT_BEGIN_NAMESPACE + +IndexWindow::IndexWindow(QWidget *parent) + : QWidget(parent) + , m_searchLineEdit(new QLineEdit) + , m_indexWidget(HelpEngineWrapper::instance().indexWidget()) +{ + TRACE_OBJ + QVBoxLayout *layout = new QVBoxLayout(this); + QLabel *l = new QLabel(tr("&Look for:")); + layout->addWidget(l); + + l->setBuddy(m_searchLineEdit); + connect(m_searchLineEdit, SIGNAL(textChanged(QString)), this, + SLOT(filterIndices(QString))); + m_searchLineEdit->installEventFilter(this); + layout->setMargin(4); + layout->addWidget(m_searchLineEdit); + + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + m_indexWidget->installEventFilter(this); + connect(helpEngine.indexModel(), SIGNAL(indexCreationStarted()), this, + SLOT(disableSearchLineEdit())); + connect(helpEngine.indexModel(), SIGNAL(indexCreated()), this, + SLOT(enableSearchLineEdit())); + connect(m_indexWidget, SIGNAL(linkActivated(QUrl,QString)), this, + SIGNAL(linkActivated(QUrl))); + connect(m_indexWidget, SIGNAL(linksActivated(QMap<QString,QUrl>,QString)), + this, SIGNAL(linksActivated(QMap<QString,QUrl>,QString))); + connect(m_searchLineEdit, SIGNAL(returnPressed()), m_indexWidget, + SLOT(activateCurrentItem())); + layout->addWidget(m_indexWidget); + + m_indexWidget->viewport()->installEventFilter(this); +} + +IndexWindow::~IndexWindow() +{ + TRACE_OBJ +} + +void IndexWindow::filterIndices(const QString &filter) +{ + TRACE_OBJ + if (filter.contains(QLatin1Char('*'))) + m_indexWidget->filterIndices(filter, filter); + else + m_indexWidget->filterIndices(filter, QString()); +} + +bool IndexWindow::eventFilter(QObject *obj, QEvent *e) +{ + TRACE_OBJ + if (obj == m_searchLineEdit && e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + QModelIndex idx = m_indexWidget->currentIndex(); + switch (ke->key()) { + case Qt::Key_Up: + idx = m_indexWidget->model()->index(idx.row()-1, + idx.column(), idx.parent()); + if (idx.isValid()) { + m_indexWidget->setCurrentIndex(idx); + return true; + } + break; + case Qt::Key_Down: + idx = m_indexWidget->model()->index(idx.row()+1, + idx.column(), idx.parent()); + if (idx.isValid()) { + m_indexWidget->setCurrentIndex(idx); + return true; + } + break; + case Qt::Key_Escape: + emit escapePressed(); + return true; + default: ; // stop complaining + } + } else if (obj == m_indexWidget && e->type() == QEvent::ContextMenu) { + QContextMenuEvent *ctxtEvent = static_cast<QContextMenuEvent*>(e); + QModelIndex idx = m_indexWidget->indexAt(ctxtEvent->pos()); + if (idx.isValid()) { + QMenu menu; + QAction *curTab = menu.addAction(tr("Open Link")); + QAction *newTab = menu.addAction(tr("Open Link in New Tab")); + menu.move(m_indexWidget->mapToGlobal(ctxtEvent->pos())); + + QAction *action = menu.exec(); + if (curTab == action) + m_indexWidget->activateCurrentItem(); + else if (newTab == action) { + open(m_indexWidget, idx); + } + } + } else if (m_indexWidget && obj == m_indexWidget->viewport() + && e->type() == QEvent::MouseButtonRelease) { + QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(e); + QModelIndex idx = m_indexWidget->indexAt(mouseEvent->pos()); + if (idx.isValid()) { + Qt::MouseButtons button = mouseEvent->button(); + if (((button == Qt::LeftButton) && (mouseEvent->modifiers() & Qt::ControlModifier)) + || (button == Qt::MidButton)) { + open(m_indexWidget, idx); + } + } + } +#ifdef Q_OS_MAC + else if (obj == m_indexWidget && e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(e); + if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) + m_indexWidget->activateCurrentItem(); + } +#endif + return QWidget::eventFilter(obj, e); +} + +void IndexWindow::enableSearchLineEdit() +{ + TRACE_OBJ + m_searchLineEdit->setDisabled(false); + filterIndices(m_searchLineEdit->text()); +} + +void IndexWindow::disableSearchLineEdit() +{ + TRACE_OBJ + m_searchLineEdit->setDisabled(true); +} + +void IndexWindow::setSearchLineEditText(const QString &text) +{ + TRACE_OBJ + m_searchLineEdit->setText(text); +} + +void IndexWindow::focusInEvent(QFocusEvent *e) +{ + TRACE_OBJ + if (e->reason() != Qt::MouseFocusReason) { + m_searchLineEdit->selectAll(); + m_searchLineEdit->setFocus(); + } +} + +void IndexWindow::open(QHelpIndexWidget* indexWidget, const QModelIndex &index) +{ + TRACE_OBJ + QHelpIndexModel *model = qobject_cast<QHelpIndexModel*>(indexWidget->model()); + if (model) { + QString keyword = model->data(index, Qt::DisplayRole).toString(); + QMap<QString, QUrl> links = model->linksForKeyword(keyword); + + QUrl url; + if (links.count() > 1) { + TopicChooser tc(this, keyword, links); + if (tc.exec() == QDialog::Accepted) + url = tc.link(); + } else if (links.count() == 1) { + url = links.constBegin().value(); + } else { + return; + } + + if (!HelpViewer::canOpenPage(url.path())) + CentralWidget::instance()->setSource(url); + else + OpenPagesManager::instance()->createPage(url); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/indexwindow.h b/src/assistant/tools/assistant/indexwindow.h new file mode 100644 index 000000000..6c1c4e14e --- /dev/null +++ b/src/assistant/tools/assistant/indexwindow.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INDEXWINDOW_H +#define INDEXWINDOW_H + +#include <QtCore/QUrl> +#include <QtGui/QWidget> +#include <QtGui/QLineEdit> + +QT_BEGIN_NAMESPACE + +class QHelpIndexWidget; +class QModelIndex; + +class IndexWindow : public QWidget +{ + Q_OBJECT + +public: + IndexWindow(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); + void open(QHelpIndexWidget *indexWidget, const QModelIndex &index); + + QLineEdit *m_searchLineEdit; + QHelpIndexWidget *m_indexWidget; +}; + +QT_END_NAMESPACE + +#endif // INDEXWINDOW_H diff --git a/src/assistant/tools/assistant/installdialog.cpp b/src/assistant/tools/assistant/installdialog.cpp new file mode 100644 index 000000000..29473a2f9 --- /dev/null +++ b/src/assistant/tools/assistant/installdialog.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include "installdialog.h" + +#include <QtCore/QTimer> +#include <QtCore/QUrl> +#include <QtCore/QBuffer> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QCryptographicHash> + +#include <QtGui/QMessageBox> +#include <QtGui/QFileDialog> + +#include <QtHelp/QHelpEngineCore> + +#include <QtNetwork/QHttp> + +QT_BEGIN_NAMESPACE +#ifndef QT_NO_HTTP + +#define QCH_FILENAME 92943 +#define QCH_NAMESPACE 92944 +#define QCH_CHECKSUM 92945 + +InstallDialog::InstallDialog(QHelpEngineCore *helpEngine, QWidget *parent, + const QString &host, int port) + : QDialog(parent), m_helpEngine(helpEngine), m_host(host), m_port(port) +{ + TRACE_OBJ + m_ui.setupUi(this); + + m_ui.installButton->setEnabled(false); + m_ui.cancelButton->setEnabled(false); + m_ui.pathLineEdit->setText(QFileInfo(m_helpEngine->collectionFile()).absolutePath()); + m_ui.progressBar->hide(); + + m_windowTitle = tr("Install Documentation"); + + m_http = new QHttp(this); + connect(m_http, SIGNAL(requestFinished(int,bool)), + this, SLOT(httpRequestFinished(int,bool))); + connect(m_http, SIGNAL(dataReadProgress(int,int)), + this, SLOT(updateDataReadProgress(int,int))); + connect(m_http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), + this, SLOT(readResponseHeader(QHttpResponseHeader))); + connect(m_ui.installButton, SIGNAL(clicked()), this, SLOT(install())); + connect(m_ui.cancelButton, SIGNAL(clicked()), this, SLOT(cancelDownload())); + connect(m_ui.browseButton, SIGNAL(clicked()), this, SLOT(browseDirectories())); + + connect(m_ui.listWidget, SIGNAL(itemChanged(QListWidgetItem*)), + this, SLOT(updateInstallButton())); + + QTimer::singleShot(0, this, SLOT(init())); +} + +InstallDialog::~InstallDialog() +{ + TRACE_OBJ +} + +QStringList InstallDialog::installedDocumentations() const +{ + TRACE_OBJ + return m_installedDocumentations; +} + +void InstallDialog::init() +{ + TRACE_OBJ + m_ui.statusLabel->setText(tr("Downloading documentation info...")); + m_ui.progressBar->show(); + + QUrl url(QLatin1String("http://qt.nokia.com/doc/assistantdocs/docs.txt")); + m_buffer = new QBuffer(); + m_buffer->open(QBuffer::ReadWrite); + + if (m_port > -1) + m_http->setProxy(m_host, m_port); + m_http->setHost(url.host()); + m_httpAborted = false; + m_docInfoId = m_http->get(url.path(), m_buffer); + + m_ui.cancelButton->setEnabled(true); + m_ui.closeButton->setEnabled(false); +} + +void InstallDialog::updateInstallButton() +{ + TRACE_OBJ + QListWidgetItem *item = 0; + for (int i=0; i<m_ui.listWidget->count(); ++i) { + item = m_ui.listWidget->item(i); + if (item->checkState() == Qt::Checked + && item->flags() & Qt::ItemIsEnabled) { + m_ui.installButton->setEnabled(true); + return; + } + } + m_ui.installButton->setEnabled(false); +} + +void InstallDialog::updateDocItemList() +{ + TRACE_OBJ + QStringList registeredDocs = m_helpEngine->registeredDocumentations(); + QListWidgetItem *item = 0; + for (int i=0; i<m_ui.listWidget->count(); ++i) { + item = m_ui.listWidget->item(i); + QString ns = item->data(QCH_NAMESPACE).toString(); + if (!ns.isEmpty() && registeredDocs.contains(ns)) { + item->setFlags(Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); + } + item->setCheckState(Qt::Unchecked); + } +} + +void InstallDialog::cancelDownload() +{ + TRACE_OBJ + m_ui.statusLabel->setText(tr("Download canceled.")); + m_httpAborted = true; + m_itemsToInstall.clear(); + m_http->abort(); + m_ui.cancelButton->setEnabled(false); + m_ui.closeButton->setEnabled(true); + updateInstallButton(); +} + +void InstallDialog::install() +{ + TRACE_OBJ + QListWidgetItem *item = 0; + for (int i=0; i<m_ui.listWidget->count(); ++i) { + item = m_ui.listWidget->item(i); + if (item->checkState() == Qt::Checked) + m_itemsToInstall.append(item); + } + m_ui.installButton->setEnabled(false); + downloadNextFile(); +} + +void InstallDialog::downloadNextFile() +{ + TRACE_OBJ + if (!m_itemsToInstall.count()) { + m_ui.cancelButton->setEnabled(false); + m_ui.closeButton->setEnabled(true); + m_ui.statusLabel->setText(tr("Done.")); + m_ui.progressBar->hide(); + updateDocItemList(); + updateInstallButton(); + return; + } + + QListWidgetItem *item = m_itemsToInstall.dequeue(); + m_currentCheckSum = item->data(QCH_CHECKSUM).toString(); + QString fileName = item->data(QCH_FILENAME).toString(); + QString saveFileName = m_ui.pathLineEdit->text() + QDir::separator() + + fileName; + + if (QFile::exists(saveFileName) + && QMessageBox::information(this, m_windowTitle, + tr("The file %1 already exists. Do you want to overwrite it?") + .arg(saveFileName), QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes) == QMessageBox::No) { + installFile(saveFileName); + downloadNextFile(); + return; + } + + m_file = new QFile(saveFileName); + if (!m_file->open(QIODevice::WriteOnly|QIODevice::Truncate)) { + QMessageBox::information(this, m_windowTitle, + tr("Unable to save the file %1: %2.") + .arg(saveFileName).arg(m_file->errorString())); + delete m_file; + m_file = 0; + downloadNextFile(); + return; + } + + m_ui.statusLabel->setText(tr("Downloading %1...").arg(fileName)); + m_ui.progressBar->show(); + + QLatin1String urlStr("http://qt.nokia.com/doc/assistantdocs/%1"); + QUrl url(QString(urlStr).arg(fileName)); + + m_httpAborted = false; + m_docId = m_http->get(url.path(), m_file); + + m_ui.cancelButton->setEnabled(true); + m_ui.closeButton->setEnabled(false); +} + +void InstallDialog::httpRequestFinished(int requestId, bool error) +{ + TRACE_OBJ + if (requestId == m_docInfoId && m_buffer) { + m_ui.progressBar->hide(); + if (error) { + QMessageBox::information(this, m_windowTitle, + tr("Download failed: %1.") + .arg(m_http->errorString())); + } else if (!m_httpAborted) { + QStringList registeredDocs = m_helpEngine->registeredDocumentations(); + m_buffer->seek(0); + while (m_buffer->canReadLine()) { + QByteArray ba = m_buffer->readLine(); + QStringList lst = QString::fromAscii(ba.constData()).split(QLatin1Char('|')); + if (lst.count() != 4) { + QMessageBox::information(this, m_windowTitle, + tr("Documentation info file is corrupt!")); + } else { + QListWidgetItem *item = new QListWidgetItem(m_ui.listWidget); + item->setText(lst.at(2).trimmed()); + item->setData(QCH_FILENAME, lst.first()); + item->setData(QCH_NAMESPACE, lst.at(1)); + item->setData(QCH_CHECKSUM, lst.last().trimmed()); + } + } + updateDocItemList(); + } + if (m_buffer) + m_buffer->close(); + delete m_buffer; + m_buffer = 0; + m_ui.statusLabel->setText(tr("Done.")); + m_ui.cancelButton->setEnabled(false); + m_ui.closeButton->setEnabled(true); + updateInstallButton(); + } else if (requestId == m_docId) { + m_file->close(); + if (!m_httpAborted) { + QString checkSum; + if (m_file->open(QIODevice::ReadOnly)) { + QByteArray digest = QCryptographicHash::hash(m_file->readAll(), + QCryptographicHash::Md5); + m_file->close(); + checkSum = QString::fromLatin1(digest.toHex()); + } + if (error) { + m_file->remove(); + QMessageBox::warning(this, m_windowTitle, + tr("Download failed: %1.") + .arg(m_http->errorString())); + } else if (checkSum.isEmpty() || m_currentCheckSum != checkSum) { + m_file->remove(); + QMessageBox::warning(this, m_windowTitle, + tr("Download failed: Downloaded file is corrupted.")); + } else { + m_ui.statusLabel->setText(tr("Installing documentation %1...") + .arg(QFileInfo(m_file->fileName()).fileName())); + m_ui.progressBar->setMaximum(0); + m_ui.statusLabel->setText(tr("Done.")); + installFile(m_file->fileName()); + } + } else { + m_file->remove(); + } + delete m_file; + m_file = 0; + downloadNextFile(); + } +} + +void InstallDialog::installFile(const QString &fileName) +{ + TRACE_OBJ + if (m_helpEngine->registerDocumentation(fileName)) { + m_installedDocumentations + .append(QHelpEngineCore::namespaceName(fileName)); + } else { + QMessageBox::information(this, m_windowTitle, + tr("Error while installing documentation:\n%1") + .arg(m_helpEngine->error())); + } +} + +void InstallDialog::readResponseHeader(const QHttpResponseHeader &responseHeader) +{ + TRACE_OBJ + if (responseHeader.statusCode() != 200) { + QMessageBox::information(this, m_windowTitle, + tr("Download failed: %1.") + .arg(responseHeader.reasonPhrase())); + m_httpAborted = true; + m_ui.progressBar->hide(); + m_http->abort(); + return; + } +} + +void InstallDialog::updateDataReadProgress(int bytesRead, int totalBytes) +{ + TRACE_OBJ + if (m_httpAborted) + return; + + m_ui.progressBar->setMaximum(totalBytes); + m_ui.progressBar->setValue(bytesRead); +} + +void InstallDialog::browseDirectories() +{ + TRACE_OBJ + QString dir = QFileDialog::getExistingDirectory(this, m_windowTitle, + m_ui.pathLineEdit->text()); + if (!dir.isEmpty()) + m_ui.pathLineEdit->setText(dir); +} + +#endif +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/installdialog.h b/src/assistant/tools/assistant/installdialog.h new file mode 100644 index 000000000..0fb45a130 --- /dev/null +++ b/src/assistant/tools/assistant/installdialog.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef INSTALLDIALOG_H +#define INSTALLDIALOG_H + +#include <QtCore/QQueue> +#include <QtGui/QDialog> +#include <QtNetwork/QHttpResponseHeader> +#include "ui_installdialog.h" + +#ifndef QT_NO_HTTP + +QT_BEGIN_NAMESPACE + +class QHttp; +class QBuffer; +class QFile; +class QHelpEngineCore; + +class InstallDialog : public QDialog +{ + Q_OBJECT + +public: + explicit InstallDialog(QHelpEngineCore *helpEngine, QWidget *parent = 0, + const QString &host = QString(), int port = -1); + ~InstallDialog(); + + QStringList installedDocumentations() const; + +private slots: + void init(); + void cancelDownload(); + void install(); + void httpRequestFinished(int requestId, bool error); + void readResponseHeader(const QHttpResponseHeader &responseHeader); + void updateDataReadProgress(int bytesRead, int totalBytes); + void updateInstallButton(); + void browseDirectories(); + +private: + void downloadNextFile(); + void updateDocItemList(); + void installFile(const QString &fileName); + + Ui::InstallDialog m_ui; + QHelpEngineCore *m_helpEngine; + QHttp *m_http; + QBuffer *m_buffer; + QFile *m_file; + bool m_httpAborted; + int m_docInfoId; + int m_docId; + QQueue<QListWidgetItem*> m_itemsToInstall; + QString m_currentCheckSum; + QString m_windowTitle; + QStringList m_installedDocumentations; + QString m_host; + int m_port; +}; + +QT_END_NAMESPACE + +#endif + +#endif // INSTALLDIALOG_H diff --git a/src/assistant/tools/assistant/installdialog.ui b/src/assistant/tools/assistant/installdialog.ui new file mode 100644 index 000000000..21a05da77 --- /dev/null +++ b/src/assistant/tools/assistant/installdialog.ui @@ -0,0 +1,118 @@ +<ui version="4.0" > + <class>InstallDialog</class> + <widget class="QDialog" name="InstallDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>436</width> + <height>245</height> + </rect> + </property> + <property name="windowTitle" > + <string>Install Documentation</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="4" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Available Documentation:</string> + </property> + </widget> + </item> + <item rowspan="4" row="1" column="0" colspan="4" > + <widget class="QListWidget" name="listWidget" /> + </item> + <item row="1" column="4" > + <widget class="QPushButton" name="installButton" > + <property name="text" > + <string>Install</string> + </property> + </widget> + </item> + <item row="2" column="4" > + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + <item row="3" column="4" > + <widget class="QPushButton" name="closeButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + <item row="4" column="4" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>56</height> + </size> + </property> + </spacer> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Installation Path:</string> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2" > + <widget class="QLineEdit" name="pathLineEdit" /> + </item> + <item row="5" column="3" > + <widget class="QToolButton" name="browseButton" > + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + <item row="6" column="0" colspan="5" > + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="7" column="0" colspan="2" > + <widget class="QLabel" name="statusLabel" /> + </item> + <item row="7" column="2" colspan="3" > + <widget class="QProgressBar" name="progressBar" > + <property name="value" > + <number>0</number> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>closeButton</sender> + <signal>clicked()</signal> + <receiver>InstallDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>330</x> + <y>107</y> + </hint> + <hint type="destinationlabel" > + <x>332</x> + <y>158</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/assistant/tools/assistant/main.cpp b/src/assistant/tools/assistant/main.cpp new file mode 100644 index 000000000..84972c546 --- /dev/null +++ b/src/assistant/tools/assistant/main.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QLibraryInfo> +#include <QtCore/QLocale> +#include <QtCore/QScopedPointer> +#include <QtCore/QStringList> +#include <QtCore/QTranslator> +#include <QtCore/QUrl> + +#include <QtGui/QApplication> +#include <QtGui/QDesktopServices> + +#include <QtHelp/QHelpEngine> +#include <QtHelp/QHelpSearchEngine> + +#include <QtNetwork/QLocalSocket> + +#include <QtSql/QSqlDatabase> + +#include "../shared/collectionconfiguration.h" +#include "helpenginewrapper.h" +#include "mainwindow.h" +#include "cmdlineparser.h" + +// #define TRACING_REQUESTED + +QT_USE_NAMESPACE + +#if defined(USE_STATIC_SQLITE_PLUGIN) + #include <QtPlugin> + Q_IMPORT_PLUGIN(qsqlite) +#endif + +namespace { + +void +updateLastPagesOnUnregister(QHelpEngineCore& helpEngine, const QString& nsName) +{ + TRACE_OBJ + int lastPage = CollectionConfiguration::lastTabPage(helpEngine); + QStringList currentPages = CollectionConfiguration::lastShownPages(helpEngine); + if (!currentPages.isEmpty()) { + QStringList zoomList = CollectionConfiguration::lastZoomFactors(helpEngine); + while (zoomList.count() < currentPages.count()) + zoomList.append(CollectionConfiguration::DefaultZoomFactor); + + for (int i = currentPages.count(); --i >= 0;) { + if (QUrl(currentPages.at(i)).host() == nsName) { + zoomList.removeAt(i); + currentPages.removeAt(i); + lastPage = (lastPage == (i + 1)) ? 1 : lastPage; + } + } + + CollectionConfiguration::setLastShownPages(helpEngine, currentPages); + CollectionConfiguration::setLastTabPage(helpEngine, lastPage); + CollectionConfiguration::setLastZoomFactors(helpEngine, zoomList); + } +} + +bool +updateUserCollection(QHelpEngineCore& user, const QHelpEngineCore& caller) +{ + TRACE_OBJ + if (!CollectionConfiguration::isNewer(caller, user)) + return false; + CollectionConfiguration::copyConfiguration(caller, user); + return true; +} + +void stripNonexistingDocs(QHelpEngineCore& collection) +{ + TRACE_OBJ + const QStringList &namespaces = collection.registeredDocumentations(); + foreach (const QString &ns, namespaces) { + QFileInfo fi(collection.documentationFileName(ns)); + if (!fi.exists() || !fi.isFile()) + collection.unregisterDocumentation(ns); + } +} + +QString indexFilesFolder(const QString &collectionFile) +{ + TRACE_OBJ + QString indexFilesFolder = QLatin1String(".fulltextsearch"); + if (!collectionFile.isEmpty()) { + QFileInfo fi(collectionFile); + indexFilesFolder = QLatin1Char('.') + + fi.fileName().left(fi.fileName().lastIndexOf(QLatin1String(".qhc"))); + } + return indexFilesFolder; +} + +/* + * Returns the expected absolute file path of the cached collection file + * correspondinging to the given collection's file. + * It may or may not exist yet. + */ +QString constructCachedCollectionFilePath(const QHelpEngineCore &collection) +{ + TRACE_OBJ + const QString &filePath = collection.collectionFile(); + const QString &fileName = QFileInfo(filePath).fileName(); + const QString &cacheDir = CollectionConfiguration::cacheDir(collection); + const QString &dir = !cacheDir.isEmpty() + && CollectionConfiguration::cacheDirIsRelativeToCollection(collection) + ? QFileInfo(filePath).dir().absolutePath() + + QDir::separator() + cacheDir + : MainWindow::collectionFileDirectory(false, cacheDir); + return dir + QDir::separator() + fileName; +} + +bool synchronizeDocs(QHelpEngineCore &collection, + QHelpEngineCore &cachedCollection, + CmdLineParser &cmd) +{ + TRACE_OBJ + const QDateTime &lastCollectionRegisterTime = + CollectionConfiguration::lastRegisterTime(collection); + if (!lastCollectionRegisterTime.isValid() || lastCollectionRegisterTime + < CollectionConfiguration::lastRegisterTime(cachedCollection)) + return true; + + const QStringList &docs = collection.registeredDocumentations(); + const QStringList &cachedDocs = cachedCollection.registeredDocumentations(); + + /* + * Ensure that the cached collection contains all docs that + * the collection contains. + */ + foreach (const QString &doc, docs) { + if (!cachedDocs.contains(doc)) { + const QString &docFile = collection.documentationFileName(doc); + if (!cachedCollection.registerDocumentation(docFile)) { + cmd.showMessage(QCoreApplication::translate("Assistant", + "Error registering documentation file '%1': %2"). + arg(docFile).arg(cachedCollection.error()), true); + return false; + } + } + } + + CollectionConfiguration::updateLastRegisterTime(cachedCollection); + + return true; +} + +bool removeSearchIndex(const QString &collectionFile) +{ + TRACE_OBJ + QString path = QFileInfo(collectionFile).path(); + path += QLatin1Char('/') + indexFilesFolder(collectionFile); + + QLocalSocket localSocket; + localSocket.connectToServer(QString(QLatin1String("QtAssistant%1")) + .arg(QLatin1String(QT_VERSION_STR))); + + QDir dir(path); // check if there is no other instance ruinning + if (!dir.exists() || localSocket.waitForConnected()) + return false; + + QStringList lst = dir.entryList(QDir::Files | QDir::Hidden); + foreach (const QString &item, lst) + dir.remove(item); + return true; +} + +bool rebuildSearchIndex(QCoreApplication &app, const QString &collectionFile, + CmdLineParser &cmd) +{ + TRACE_OBJ + QHelpEngine engine(collectionFile); + if (!engine.setupData()) { + cmd.showMessage(QCoreApplication::translate("Assistant", "Error: %1") + .arg(engine.error()), true); + return false; + } + + QHelpSearchEngine * const searchEngine = engine.searchEngine(); + QObject::connect(searchEngine, SIGNAL(indexingFinished()), &app, + SLOT(quit())); + searchEngine->reindexDocumentation(); + return app.exec() == 0; +} + +bool useGui(int argc, char *argv[]) +{ + TRACE_OBJ + bool gui = true; +#ifndef Q_OS_WIN + // Look for arguments that imply command-line mode. + const char * cmdModeArgs[] = { + "-help", "-register", "-unregister", "-remove-search-index", + "-rebuild-search-index" + }; + for (int i = 1; i < argc; ++i) { + for (size_t j = 0; j < sizeof cmdModeArgs/sizeof *cmdModeArgs; ++j) { + if(strcmp(argv[i], cmdModeArgs[j]) == 0) { + gui = false; + break; + } + } + } +#else + Q_UNUSED(argc) + Q_UNUSED(argv) +#endif + return gui; +} + +bool registerDocumentation(QHelpEngineCore &collection, CmdLineParser &cmd, + bool printSuccess) +{ + TRACE_OBJ + if (!collection.registerDocumentation(cmd.helpFile())) { + cmd.showMessage(QCoreApplication::translate("Assistant", + "Could not register documentation file\n%1\n\nReason:\n%2") + .arg(cmd.helpFile()).arg(collection.error()), true); + return false; + } + if (printSuccess) + cmd.showMessage(QCoreApplication::translate("Assistant", + "Documentation successfully registered."), + false); + CollectionConfiguration::updateLastRegisterTime(collection); + return true; +} + +bool unregisterDocumentation(QHelpEngineCore &collection, + const QString &namespaceName, CmdLineParser &cmd, bool printSuccess) +{ + TRACE_OBJ + if (!collection.unregisterDocumentation(namespaceName)) { + cmd.showMessage(QCoreApplication::translate("Assistant", + "Could not unregister documentation" + " file\n%1\n\nReason:\n%2"). + arg(cmd.helpFile()).arg(collection.error()), true); + return false; + } + updateLastPagesOnUnregister(collection, namespaceName); + if (printSuccess) + cmd.showMessage(QCoreApplication::translate("Assistant", + "Documentation successfully unregistered."), + false); + return true; +} + +void setupTranslation(const QString &fileName, const QString &dir) +{ + QTranslator *translator = new QTranslator(QCoreApplication::instance()); + if (translator->load(fileName, dir)) { + QCoreApplication::installTranslator(translator); + } else if (!fileName.endsWith(QLatin1String("en_US")) + && !fileName.endsWith(QLatin1String("_C"))) { + qWarning("Could not load translation file %s in directory %s.", + qPrintable(fileName), qPrintable(dir)); + } +} + +void setupTranslations() +{ + TRACE_OBJ + const QString& locale = QLocale::system().name(); + const QString &resourceDir + = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + setupTranslation(QLatin1String("assistant_") + locale, resourceDir); + setupTranslation(QLatin1String("qt_") + locale, resourceDir); + setupTranslation(QLatin1String("qt_help_") + locale, resourceDir); +} + +} // Anonymous namespace. + +int main(int argc, char *argv[]) +{ + TRACE_OBJ + QApplication a(argc, argv, useGui(argc, argv)); + a.addLibraryPath(a.applicationDirPath() + QLatin1String("/plugins")); + setupTranslations(); + + // Parse arguments. + CmdLineParser cmd(a.arguments()); + CmdLineParser::Result res = cmd.parse(); + if (res == CmdLineParser::Help) + return 0; + else if (res == CmdLineParser::Error) + return -1; + + /* + * Create the collection objects that we need. We always have the + * cached collection file. Depending on whether the user specified + * one, we also may have an input collection file. + */ + const QString collectionFile = cmd.collectionFile(); + const bool collectionFileGiven = !collectionFile.isEmpty(); + QScopedPointer<QHelpEngineCore> collection; + if (collectionFileGiven) { + collection.reset(new QHelpEngineCore(collectionFile)); + if (!collection->setupData()) { + cmd.showMessage(QCoreApplication::translate("Assistant", + "Error reading collection file '%1': %2."). + arg(collectionFile).arg(collection->error()), true); + return EXIT_FAILURE; + } + } + const QString &cachedCollectionFile = collectionFileGiven + ? constructCachedCollectionFilePath(*collection) + : MainWindow::defaultHelpCollectionFileName(); + if (collectionFileGiven && !QFileInfo(cachedCollectionFile).exists() + && !collection->copyCollectionFile(cachedCollectionFile)) { + cmd.showMessage(QCoreApplication::translate("Assistant", + "Error creating collection file '%1': %2."). + arg(cachedCollectionFile).arg(collection->error()), true); + return EXIT_FAILURE; + } + QHelpEngineCore cachedCollection(cachedCollectionFile); + if (!cachedCollection.setupData()) { + cmd.showMessage(QCoreApplication::translate("Assistant", + "Error reading collection file '%1': %2."). + arg(cachedCollectionFile). + arg(cachedCollection.error()), true); + return EXIT_FAILURE; + } + + stripNonexistingDocs(cachedCollection); + if (collectionFileGiven) { + if (CollectionConfiguration::isNewer(*collection, cachedCollection)) + CollectionConfiguration::copyConfiguration(*collection, + cachedCollection); + if (!synchronizeDocs(*collection, cachedCollection, cmd)) + return EXIT_FAILURE; + } + + if (cmd.registerRequest() != CmdLineParser::None) { + const QStringList &cachedDocs = + cachedCollection.registeredDocumentations(); + const QString &namespaceName = + QHelpEngineCore::namespaceName(cmd.helpFile()); + if (cmd.registerRequest() == CmdLineParser::Register) { + if (collectionFileGiven + && !registerDocumentation(*collection, cmd, true)) + return EXIT_FAILURE; + if (!cachedDocs.contains(namespaceName) + && !registerDocumentation(cachedCollection, cmd, !collectionFileGiven)) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + if (cmd.registerRequest() == CmdLineParser::Unregister) { + if (collectionFileGiven + && !unregisterDocumentation(*collection, namespaceName, cmd, true)) + return EXIT_FAILURE; + if (cachedDocs.contains(namespaceName) + && !unregisterDocumentation(cachedCollection, namespaceName, + cmd, !collectionFileGiven)) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + } + + if (cmd.removeSearchIndex()) { + return removeSearchIndex(cachedCollectionFile) + ? EXIT_SUCCESS : EXIT_FAILURE; + } + + if (cmd.rebuildSearchIndex()) { + return rebuildSearchIndex(a, cachedCollectionFile, cmd) + ? EXIT_SUCCESS : EXIT_FAILURE; + } + + if (!QSqlDatabase::isDriverAvailable(QLatin1String("QSQLITE"))) { + cmd.showMessage(QCoreApplication::translate("Assistant", + "Cannot load sqlite database driver!"), + true); + return EXIT_FAILURE; + } + + if (!cmd.currentFilter().isEmpty()) { + if (collectionFileGiven) + collection->setCurrentFilter(cmd.currentFilter()); + cachedCollection.setCurrentFilter(cmd.currentFilter()); + } + + if (collectionFileGiven) + cmd.setCollectionFile(cachedCollectionFile); + + MainWindow *w = new MainWindow(&cmd); + w->show(); + a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit())); + + /* + * We need to be careful here: The main window has to be deleted before + * the help engine wrapper, which has to be deleted before the + * QApplication. + */ + const int retval = a.exec(); + delete w; + HelpEngineWrapper::removeInstance(); + return retval; +} diff --git a/src/assistant/tools/assistant/mainwindow.cpp b/src/assistant/tools/assistant/mainwindow.cpp new file mode 100644 index 000000000..342fc76da --- /dev/null +++ b/src/assistant/tools/assistant/mainwindow.cpp @@ -0,0 +1,1099 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" + +#include "aboutdialog.h" +#include "bookmarkmanager.h" +#include "centralwidget.h" +#include "cmdlineparser.h" +#include "contentwindow.h" +#include "globalactions.h" +#include "helpenginewrapper.h" +#include "indexwindow.h" +#include "openpagesmanager.h" +#include "preferencesdialog.h" +#include "qtdocinstaller.h" +#include "remotecontrol.h" +#include "searchwidget.h" +#include "topicchooser.h" +#include "tracer.h" + +#include <QtCore/QByteArray> +#include <QtCore/QDateTime> +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QPair> +#include <QtCore/QResource> +#include <QtCore/QTextStream> +#include <QtCore/QTimer> + +#include <QtGui/QAction> +#include <QtGui/QComboBox> +#include <QtGui/QDesktopServices> +#include <QtGui/QDesktopWidget> +#include <QtGui/QDockWidget> +#include <QtGui/QFontDatabase> +#include <QtGui/QFileDialog> +#include <QtGui/QLabel> +#include <QtGui/QLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QMenuBar> +#include <QtGui/QMessageBox> +#include <QtGui/QProgressBar> +#include <QtGui/QShortcut> +#include <QtGui/QStatusBar> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> + +#include <QtHelp/QHelpContentModel> +#include <QtHelp/QHelpEngineCore> +#include <QtHelp/QHelpIndexModel> +#include <QtHelp/QHelpSearchEngine> + +#include <cstdlib> + +QT_BEGIN_NAMESPACE + +MainWindow::MainWindow(CmdLineParser *cmdLine, QWidget *parent) + : QMainWindow(parent) + , m_bookmarkWidget(0) + , m_filterCombo(0) + , m_toolBarMenu(0) + , m_cmdLine(cmdLine) + , m_progressWidget(0) + , m_qtDocInstaller(0) + , m_connectedInitSignals(false) +{ + TRACE_OBJ + + setToolButtonStyle(Qt::ToolButtonFollowStyle); + setDockOptions(dockOptions() | AllowNestedDocks); + + QString collectionFile; + if (usesDefaultCollection()) { + MainWindow::collectionFileDirectory(true); + collectionFile = MainWindow::defaultHelpCollectionFileName(); + } else { + collectionFile = cmdLine->collectionFile(); + } + HelpEngineWrapper &helpEngineWrapper = + HelpEngineWrapper::instance(collectionFile); + BookmarkManager *bookMarkManager = BookmarkManager::instance(); + + if (!initHelpDB(!cmdLine->collectionFileGiven())) { + qDebug("Fatal error: Help engine initialization failed. " + "Error message was: %s\nAssistant will now exit.", + qPrintable(HelpEngineWrapper::instance().error())); + std::exit(1); + } + + m_centralWidget = new CentralWidget(this); + setCentralWidget(m_centralWidget); + + m_indexWindow = new IndexWindow(this); + QDockWidget *indexDock = new QDockWidget(tr("Index"), this); + indexDock->setObjectName(QLatin1String("IndexWindow")); + indexDock->setWidget(m_indexWindow); + addDockWidget(Qt::LeftDockWidgetArea, indexDock); + + m_contentWindow = new ContentWindow; + QDockWidget *contentDock = new QDockWidget(tr("Contents"), this); + contentDock->setObjectName(QLatin1String("ContentWindow")); + contentDock->setWidget(m_contentWindow); + addDockWidget(Qt::LeftDockWidgetArea, contentDock); + + m_searchWindow = new SearchWidget(helpEngineWrapper.searchEngine()); + m_searchWindow->setFont(!helpEngineWrapper.usesBrowserFont() ? qApp->font() + : helpEngineWrapper.browserFont()); + QDockWidget *searchDock = new QDockWidget(tr("Search"), this); + searchDock->setObjectName(QLatin1String("SearchWindow")); + searchDock->setWidget(m_searchWindow); + addDockWidget(Qt::LeftDockWidgetArea, searchDock); + + QDockWidget *bookmarkDock = new QDockWidget(tr("Bookmarks"), this); + bookmarkDock->setObjectName(QLatin1String("BookmarkWindow")); + bookmarkDock->setWidget(m_bookmarkWidget + = bookMarkManager->bookmarkDockWidget()); + addDockWidget(Qt::LeftDockWidgetArea, bookmarkDock); + + QDockWidget *openPagesDock = new QDockWidget(tr("Open Pages"), this); + openPagesDock->setObjectName(QLatin1String("Open Pages")); + OpenPagesManager *openPagesManager + = OpenPagesManager::createInstance(this, usesDefaultCollection(), m_cmdLine->url()); + openPagesDock->setWidget(openPagesManager->openPagesWidget()); + addDockWidget(Qt::LeftDockWidgetArea, openPagesDock); + + connect(m_centralWidget, SIGNAL(addBookmark(QString, QString)), + bookMarkManager, SLOT(addBookmark(QString, QString))); + connect(bookMarkManager, SIGNAL(escapePressed()), this, + SLOT(activateCurrentCentralWidgetTab())); + connect(bookMarkManager, SIGNAL(setSource(QUrl)), m_centralWidget, + SLOT(setSource(QUrl))); + connect(bookMarkManager, SIGNAL(setSourceInNewTab(QUrl)), + openPagesManager, SLOT(createPage(QUrl))); + + QHelpSearchEngine *searchEngine = helpEngineWrapper.searchEngine(); + connect(searchEngine, SIGNAL(indexingStarted()), this, SLOT(indexingStarted())); + connect(searchEngine, SIGNAL(indexingFinished()), this, SLOT(indexingFinished())); + + QString defWindowTitle = tr("Qt Assistant"); + setWindowTitle(defWindowTitle); + + setupActions(); + statusBar()->show(); + m_centralWidget->connectTabBar(); + + setupFilterToolbar(); + setupAddressToolbar(); + + const QString windowTitle = helpEngineWrapper.windowTitle(); + setWindowTitle(windowTitle.isEmpty() ? defWindowTitle : windowTitle); + QByteArray iconArray = helpEngineWrapper.applicationIcon(); + if (iconArray.size() > 0) { + QPixmap pix; + pix.loadFromData(iconArray); + QIcon appIcon(pix); + qApp->setWindowIcon(appIcon); + } else { + QIcon appIcon(QLatin1String(":/trolltech/assistant/images/assistant-128.png")); + qApp->setWindowIcon(appIcon); + } + + QToolBar *toolBar = addToolBar(tr("Bookmark Toolbar")); + toolBar->setObjectName(QLatin1String("Bookmark Toolbar")); + bookMarkManager->setBookmarksToolbar(toolBar); + + // Show the widget here, otherwise the restore geometry and state won't work + // on x11. + show(); + + toolBar->hide(); + toolBarMenu()->addAction(toolBar->toggleViewAction()); + + QByteArray ba(helpEngineWrapper.mainWindow()); + if (!ba.isEmpty()) + restoreState(ba); + + ba = helpEngineWrapper.mainWindowGeometry(); + if (!ba.isEmpty()) { + restoreGeometry(ba); + } else { + tabifyDockWidget(contentDock, indexDock); + tabifyDockWidget(indexDock, bookmarkDock); + tabifyDockWidget(bookmarkDock, searchDock); + contentDock->raise(); + const QRect screen = QApplication::desktop()->screenGeometry(); + resize(4*screen.width()/5, 4*screen.height()/5); + } + + if (!helpEngineWrapper.hasFontSettings()) { + helpEngineWrapper.setUseAppFont(false); + helpEngineWrapper.setUseBrowserFont(false); + helpEngineWrapper.setAppFont(qApp->font()); + helpEngineWrapper.setAppWritingSystem(QFontDatabase::Latin); + helpEngineWrapper.setBrowserFont(qApp->font()); + helpEngineWrapper.setBrowserWritingSystem(QFontDatabase::Latin); + } else { + updateApplicationFont(); + } + + updateAboutMenuText(); + + QTimer::singleShot(0, this, SLOT(insertLastPages())); + if (m_cmdLine->enableRemoteControl()) + (void)new RemoteControl(this); + + if (m_cmdLine->contents() == CmdLineParser::Show) + showContents(); + else if (m_cmdLine->contents() == CmdLineParser::Hide) + hideContents(); + + if (m_cmdLine->index() == CmdLineParser::Show) + showIndex(); + else if (m_cmdLine->index() == CmdLineParser::Hide) + hideIndex(); + + if (m_cmdLine->bookmarks() == CmdLineParser::Show) + showBookmarksDockWidget(); + else if (m_cmdLine->bookmarks() == CmdLineParser::Hide) + hideBookmarksDockWidget(); + + if (m_cmdLine->search() == CmdLineParser::Show) + showSearch(); + else if (m_cmdLine->search() == CmdLineParser::Hide) + hideSearch(); + + if (m_cmdLine->contents() == CmdLineParser::Activate) + showContents(); + else if (m_cmdLine->index() == CmdLineParser::Activate) + showIndex(); + else if (m_cmdLine->bookmarks() == CmdLineParser::Activate) + showBookmarksDockWidget(); + + if (!m_cmdLine->currentFilter().isEmpty()) { + const QString &curFilter = m_cmdLine->currentFilter(); + if (helpEngineWrapper.customFilters().contains(curFilter)) + helpEngineWrapper.setCurrentFilter(curFilter); + } + + if (usesDefaultCollection()) + QTimer::singleShot(0, this, SLOT(lookForNewQtDocumentation())); + else + checkInitState(); + + connect(&helpEngineWrapper, SIGNAL(documentationRemoved(QString)), + this, SLOT(documentationRemoved(QString))); + connect(&helpEngineWrapper, SIGNAL(documentationUpdated(QString)), + this, SLOT(documentationUpdated(QString))); + + setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); + GlobalActions::instance()->updateActions(); + if (helpEngineWrapper.addressBarEnabled()) + showNewAddress(); +} + +MainWindow::~MainWindow() +{ + TRACE_OBJ + if (m_qtDocInstaller) + delete m_qtDocInstaller; +} + +bool MainWindow::usesDefaultCollection() const +{ + TRACE_OBJ + return m_cmdLine->collectionFile().isEmpty(); +} + +void MainWindow::closeEvent(QCloseEvent *e) +{ + TRACE_OBJ + BookmarkManager::destroy(); + HelpEngineWrapper::instance().setMainWindow(saveState()); + HelpEngineWrapper::instance().setMainWindowGeometry(saveGeometry()); + QMainWindow::closeEvent(e); +} + +bool MainWindow::initHelpDB(bool registerInternalDoc) +{ + TRACE_OBJ + HelpEngineWrapper &helpEngineWrapper = HelpEngineWrapper::instance(); + if (!helpEngineWrapper.setupData()) + return false; + + if (!registerInternalDoc) { + if (helpEngineWrapper.defaultHomePage() == QLatin1String("help")) + helpEngineWrapper.setDefaultHomePage(QLatin1String("about:blank")); + return true; + } + bool assistantInternalDocRegistered = false; + QString intern(QLatin1String("com.trolltech.com.assistantinternal-")); + foreach (const QString &ns, helpEngineWrapper.registeredDocumentations()) { + if (ns.startsWith(intern)) { + intern = ns; + assistantInternalDocRegistered = true; + break; + } + } + + const QString &collectionFile = helpEngineWrapper.collectionFile(); + QFileInfo fi(collectionFile); + QString helpFile; + QTextStream(&helpFile) << fi.absolutePath() << QDir::separator() + << QLatin1String("assistant.qch.") << (QT_VERSION >> 16) + << QLatin1Char('.') << ((QT_VERSION >> 8) & 0xFF); + + bool needsSetup = false; + if (!assistantInternalDocRegistered || !QFile::exists(helpFile)) { + QFile file(helpFile); + if (file.open(QIODevice::WriteOnly)) { + QResource res(QLatin1String(":/trolltech/assistant/assistant.qch")); + if (file.write((const char*)res.data(), res.size()) != res.size()) + qDebug() << QLatin1String("could not write assistant.qch..."); + + file.close(); + } + helpEngineWrapper.unregisterDocumentation(intern); + helpEngineWrapper.registerDocumentation(helpFile); + needsSetup = true; + } + + if (needsSetup) + helpEngineWrapper.setupData(); + return true; +} + +void MainWindow::lookForNewQtDocumentation() +{ + TRACE_OBJ + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + QStringList docs; + docs << QLatin1String("assistant") + << QLatin1String("designer") + << QLatin1String("linguist") + << QLatin1String("qmake") + << QLatin1String("qt"); + QList<QtDocInstaller::DocInfo> qtDocInfos; + foreach (const QString &doc, docs) + qtDocInfos.append(QtDocInstaller::DocInfo(doc, helpEngine.qtDocInfo(doc))); + + m_qtDocInstaller = new QtDocInstaller(qtDocInfos); + connect(m_qtDocInstaller, SIGNAL(docsInstalled(bool)), this, + SLOT(qtDocumentationInstalled())); + connect(m_qtDocInstaller, SIGNAL(qchFileNotFound(QString)), this, + SLOT(resetQtDocInfo(QString))); + connect(m_qtDocInstaller, SIGNAL(registerDocumentation(QString, QString)), + this, SLOT(registerDocumentation(QString, QString))); + if (helpEngine.qtDocInfo(QLatin1String("qt")).count() != 2) + statusBar()->showMessage(tr("Looking for Qt Documentation...")); + m_qtDocInstaller->installDocs(); +} + +void MainWindow::qtDocumentationInstalled() +{ + TRACE_OBJ + statusBar()->clearMessage(); + checkInitState(); +} + +void MainWindow::checkInitState() +{ + TRACE_OBJ + HelpEngineWrapper::instance().initialDocSetupDone(); + if (!m_cmdLine->enableRemoteControl()) + return; + + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + if (helpEngine.contentModel()->isCreatingContents() + || helpEngine.indexModel()->isCreatingIndex()) { + if (!m_connectedInitSignals) { + connect(helpEngine.contentModel(), SIGNAL(contentsCreated()), + this, SLOT(checkInitState())); + connect(helpEngine.indexModel(), SIGNAL(indexCreated()), this, + SLOT(checkInitState())); + m_connectedInitSignals = true; + } + } else { + if (m_connectedInitSignals) { + disconnect(helpEngine.contentModel(), 0, this, 0); + disconnect(helpEngine.indexModel(), 0, this, 0); + } + emit initDone(); + } +} + +void MainWindow::insertLastPages() +{ + TRACE_OBJ + if (m_cmdLine->search() == CmdLineParser::Activate) + showSearch(); +} + +void MainWindow::setupActions() +{ + TRACE_OBJ + QString resourcePath = QLatin1String(":/trolltech/assistant/images/"); +#ifdef Q_OS_MAC + setUnifiedTitleAndToolBarOnMac(true); + resourcePath.append(QLatin1String("mac")); +#else + resourcePath.append(QLatin1String("win")); +#endif + + QMenu *menu = menuBar()->addMenu(tr("&File")); + + OpenPagesManager * const openPages = OpenPagesManager::instance(); + m_newTabAction + = menu->addAction(tr("New &Tab"), openPages, SLOT(createPage())); + m_newTabAction->setShortcut(QKeySequence::AddTab); + m_closeTabAction = menu->addAction(tr("&Close Tab"), + openPages, SLOT(closeCurrentPage())); + m_closeTabAction->setShortcuts(QKeySequence::Close); + + menu->addSeparator(); + + m_pageSetupAction = menu->addAction(tr("Page Set&up..."), m_centralWidget, + SLOT(pageSetup())); + m_printPreviewAction = menu->addAction(tr("Print Preview..."), m_centralWidget, + SLOT(printPreview())); + + GlobalActions *globalActions = GlobalActions::instance(this); + menu->addAction(globalActions->printAction()); + menu->addSeparator(); + + QIcon appExitIcon = QIcon::fromTheme("application-exit"); + QAction *tmp; +#ifdef Q_OS_WIN + tmp = menu->addAction(appExitIcon, tr("E&xit"), this, SLOT(close())); + tmp->setShortcut(QKeySequence(tr("CTRL+Q"))); +#else + tmp = menu->addAction(appExitIcon, tr("&Quit"), this, SLOT(close())); + tmp->setShortcut(QKeySequence::Quit); +#endif + tmp->setMenuRole(QAction::QuitRole); + + menu = menuBar()->addMenu(tr("&Edit")); + menu->addAction(globalActions->copyAction()); + menu->addAction(globalActions->findAction()); + + QAction *findNextAction = menu->addAction(tr("Find &Next"), m_centralWidget, + SLOT(findNext())); + findNextAction->setShortcuts(QKeySequence::FindNext); + + QAction *findPreviousAction = menu->addAction(tr("Find &Previous"), + m_centralWidget, SLOT(findPrevious())); + findPreviousAction->setShortcuts(QKeySequence::FindPrevious); + + menu->addSeparator(); + tmp = menu->addAction(tr("Preferences..."), this, SLOT(showPreferences())); + tmp->setMenuRole(QAction::PreferencesRole); + + m_viewMenu = menuBar()->addMenu(tr("&View")); + m_viewMenu->addAction(globalActions->zoomInAction()); + m_viewMenu->addAction(globalActions->zoomOutAction()); + + m_resetZoomAction = m_viewMenu->addAction(tr("Normal &Size"), m_centralWidget, + SLOT(resetZoom())); + m_resetZoomAction->setPriority(QAction::LowPriority); + m_resetZoomAction->setIcon(QIcon(resourcePath + QLatin1String("/resetzoom.png"))); + m_resetZoomAction->setShortcut(tr("Ctrl+0")); + + m_viewMenu->addSeparator(); + + m_viewMenu->addAction(tr("Contents"), this, SLOT(showContents()), + QKeySequence(tr("ALT+C"))); + m_viewMenu->addAction(tr("Index"), this, SLOT(showIndex()), + QKeySequence(tr("ALT+I"))); + m_viewMenu->addAction(tr("Bookmarks"), this, SLOT(showBookmarksDockWidget()), + QKeySequence(tr("ALT+O"))); + m_viewMenu->addAction(tr("Search"), this, SLOT(showSearch()), + QKeySequence(tr("ALT+S"))); + m_viewMenu->addAction(tr("Open Pages"), this, SLOT(showOpenPages()), + QKeySequence(tr("ALT+P"))); + + menu = menuBar()->addMenu(tr("&Go")); + menu->addAction(globalActions->homeAction()); + menu->addAction(globalActions->backAction()); + menu->addAction(globalActions->nextAction()); + + m_syncAction = menu->addAction(tr("Sync with Table of Contents"), this, + SLOT(syncContents())); + m_syncAction->setIconText(tr("Sync")); + m_syncAction->setIcon(QIcon(resourcePath + QLatin1String("/synctoc.png"))); + + menu->addSeparator(); + + tmp = menu->addAction(tr("Next Page"), openPages, SLOT(nextPage())); + tmp->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Alt+Right")) + << QKeySequence(Qt::CTRL + Qt::Key_PageDown)); + + tmp = menu->addAction(tr("Previous Page"), openPages, SLOT(previousPage())); + tmp->setShortcuts(QList<QKeySequence>() << QKeySequence(tr("Ctrl+Alt+Left")) + << QKeySequence(Qt::CTRL + Qt::Key_PageUp)); + +#ifdef Q_WS_MAC + QShortcut *sct = new QShortcut(QKeySequence(Qt::ALT + Qt::Key_Tab), this); + connect(sct, SIGNAL(activated()), openPages, SLOT(nextPageWithSwitcher())); + sct = new QShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::Key_Tab), this); + connect(sct, SIGNAL(activated()), openPages, SLOT(previousPageWithSwitcher())); +#else + QShortcut *sct = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Tab), this); + connect(sct, SIGNAL(activated()), openPages, SLOT(nextPageWithSwitcher())); + sct = new QShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab), this); + connect(sct, SIGNAL(activated()), openPages, SLOT(previousPageWithSwitcher())); +#endif + + BookmarkManager::instance()->setBookmarksMenu(menuBar()->addMenu(tr("&Bookmarks"))); + + menu = menuBar()->addMenu(tr("&Help")); + m_aboutAction = menu->addAction(tr("About..."), this, SLOT(showAboutDialog())); + m_aboutAction->setMenuRole(QAction::AboutRole); + +#ifdef Q_WS_X11 + m_resetZoomAction->setIcon(QIcon::fromTheme("zoom-original" , m_resetZoomAction->icon())); + m_syncAction->setIcon(QIcon::fromTheme("view-refresh" , m_syncAction->icon())); +#endif + + QToolBar *navigationBar = addToolBar(tr("Navigation Toolbar")); + navigationBar->setObjectName(QLatin1String("NavigationToolBar")); + navigationBar->addAction(globalActions->backAction()); + navigationBar->addAction(globalActions->nextAction()); + navigationBar->addAction(globalActions->homeAction()); + navigationBar->addAction(m_syncAction); + navigationBar->addSeparator(); + navigationBar->addAction(globalActions->copyAction()); + navigationBar->addAction(globalActions->printAction()); + navigationBar->addAction(globalActions->findAction()); + navigationBar->addSeparator(); + navigationBar->addAction(globalActions->zoomInAction()); + navigationBar->addAction(globalActions->zoomOutAction()); + navigationBar->addAction(m_resetZoomAction); + +#if defined(Q_WS_MAC) + QMenu *windowMenu = new QMenu(tr("&Window"), this); + menuBar()->insertMenu(menu->menuAction(), windowMenu); + windowMenu->addAction(tr("Zoom"), this, SLOT(showMaximized())); + windowMenu->addAction(tr("Minimize"), this, SLOT(showMinimized()), + QKeySequence(tr("Ctrl+M"))); +#endif + + // content viewer connections + connect(m_centralWidget, SIGNAL(copyAvailable(bool)), globalActions, + SLOT(setCopyAvailable(bool))); + connect(m_centralWidget, SIGNAL(currentViewerChanged()), globalActions, + SLOT(updateActions())); + connect(m_centralWidget, SIGNAL(forwardAvailable(bool)), globalActions, + SLOT(updateActions())); + connect(m_centralWidget, SIGNAL(backwardAvailable(bool)), globalActions, + SLOT(updateActions())); + connect(m_centralWidget, SIGNAL(highlighted(QString)), statusBar(), + SLOT(showMessage(QString))); + + // index window + connect(m_indexWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget, + SLOT(setSource(QUrl))); + connect(m_indexWindow, SIGNAL(linksActivated(QMap<QString,QUrl>,QString)), + this, SLOT(showTopicChooser(QMap<QString,QUrl>,QString))); + connect(m_indexWindow, SIGNAL(escapePressed()), this, + SLOT(activateCurrentCentralWidgetTab())); + + // content window + connect(m_contentWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget, + SLOT(setSource(QUrl))); + connect(m_contentWindow, SIGNAL(escapePressed()), this, + SLOT(activateCurrentCentralWidgetTab())); + + // search window + connect(m_searchWindow, SIGNAL(requestShowLink(QUrl)), + CentralWidget::instance(), SLOT(setSourceFromSearch(QUrl))); + connect(m_searchWindow, SIGNAL(requestShowLinkInNewTab(QUrl)), + OpenPagesManager::instance(), SLOT(createNewPageFromSearch(QUrl))); + +#if defined(QT_NO_PRINTER) + m_pageSetupAction->setVisible(false); + m_printPreviewAction->setVisible(false); + m_printAction->setVisible(false); +#endif +} + +QMenu *MainWindow::toolBarMenu() +{ + TRACE_OBJ + if (!m_toolBarMenu) { + m_viewMenu->addSeparator(); + m_toolBarMenu = m_viewMenu->addMenu(tr("Toolbars")); + } + return m_toolBarMenu; +} + +void MainWindow::setupFilterToolbar() +{ + TRACE_OBJ + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + if (!helpEngine.filterFunctionalityEnabled()) + return; + + m_filterCombo = new QComboBox(this); + m_filterCombo->setMinimumWidth(QFontMetrics(QFont()). + width(QLatin1String("MakeTheComboBoxWidthEnough"))); + + QToolBar *filterToolBar = addToolBar(tr("Filter Toolbar")); + filterToolBar->setObjectName(QLatin1String("FilterToolBar")); + filterToolBar->addWidget(new QLabel(tr("Filtered by:").append(QLatin1Char(' ')), + this)); + filterToolBar->addWidget(m_filterCombo); + + if (!helpEngine.filterToolbarVisible()) + filterToolBar->hide(); + toolBarMenu()->addAction(filterToolBar->toggleViewAction()); + + connect(&helpEngine, SIGNAL(setupFinished()), this, + SLOT(setupFilterCombo()), Qt::QueuedConnection); + connect(m_filterCombo, SIGNAL(activated(QString)), this, + SLOT(filterDocumentation(QString))); + connect(&helpEngine, SIGNAL(currentFilterChanged(QString)), this, + SLOT(currentFilterChanged(QString))); + + setupFilterCombo(); +} + +void MainWindow::setupAddressToolbar() +{ + TRACE_OBJ + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + if (!helpEngine.addressBarEnabled()) + return; + + m_addressLineEdit = new QLineEdit(this); + QToolBar *addressToolBar = addToolBar(tr("Address Toolbar")); + addressToolBar->setObjectName(QLatin1String("AddressToolBar")); + insertToolBarBreak(addressToolBar); + + addressToolBar->addWidget(new QLabel(tr("Address:").append(QLatin1String(" ")), + this)); + addressToolBar->addWidget(m_addressLineEdit); + + if (!helpEngine.addressBarVisible()) + addressToolBar->hide(); + toolBarMenu()->addAction(addressToolBar->toggleViewAction()); + + // address lineedit + connect(m_addressLineEdit, SIGNAL(returnPressed()), this, + SLOT(gotoAddress())); + connect(m_centralWidget, SIGNAL(currentViewerChanged()), this, + SLOT(showNewAddress())); + connect(m_centralWidget, SIGNAL(sourceChanged(QUrl)), this, + SLOT(showNewAddress(QUrl))); +} + +void MainWindow::updateAboutMenuText() +{ + TRACE_OBJ + QByteArray ba = HelpEngineWrapper::instance().aboutMenuTexts(); + if (ba.size() > 0) { + QString lang; + QString str; + QString trStr; + QString currentLang = QLocale::system().name(); + int i = currentLang.indexOf(QLatin1Char('_')); + if (i > -1) + currentLang = currentLang.left(i); + QDataStream s(&ba, QIODevice::ReadOnly); + while (!s.atEnd()) { + s >> lang; + s >> str; + if (lang == QLatin1String("default") && trStr.isEmpty()) { + trStr = str; + } else if (lang == currentLang) { + trStr = str; + break; + } + } + if (!trStr.isEmpty()) + m_aboutAction->setText(trStr); + } +} + +void MainWindow::showNewAddress() +{ + TRACE_OBJ + showNewAddress(m_centralWidget->currentSource()); +} + +void MainWindow::showNewAddress(const QUrl &url) +{ + TRACE_OBJ + m_addressLineEdit->setText(url.toString()); +} + +void MainWindow::gotoAddress() +{ + TRACE_OBJ + m_centralWidget->setSource(m_addressLineEdit->text()); +} + +void MainWindow::showTopicChooser(const QMap<QString, QUrl> &links, + const QString &keyword) +{ + TRACE_OBJ + TopicChooser tc(this, keyword, links); + if (tc.exec() == QDialog::Accepted) { + m_centralWidget->setSource(tc.link()); + } +} + +void MainWindow::showPreferences() +{ + TRACE_OBJ + PreferencesDialog dia(this); + connect(&dia, SIGNAL(updateApplicationFont()), this, + SLOT(updateApplicationFont())); + connect(&dia, SIGNAL(updateBrowserFont()), m_centralWidget, + SLOT(updateBrowserFont())); + connect(&dia, SIGNAL(updateUserInterface()), m_centralWidget, + SLOT(updateUserInterface())); + dia.showDialog(); +} + +void MainWindow::syncContents() +{ + TRACE_OBJ + qApp->setOverrideCursor(QCursor(Qt::WaitCursor)); + const QUrl url = m_centralWidget->currentSource(); + showContents(); + if (!m_contentWindow->syncToContent(url)) + statusBar()->showMessage( + tr("Could not find the associated content item."), 3000); + qApp->restoreOverrideCursor(); +} + +void MainWindow::showAboutDialog() +{ + TRACE_OBJ + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + QByteArray contents; + QByteArray ba = helpEngine.aboutTexts(); + if (!ba.isEmpty()) { + QString lang; + QByteArray cba; + QString currentLang = QLocale::system().name(); + int i = currentLang.indexOf(QLatin1Char('_')); + if (i > -1) + currentLang = currentLang.left(i); + QDataStream s(&ba, QIODevice::ReadOnly); + while (!s.atEnd()) { + s >> lang; + s >> cba; + if (lang == QLatin1String("default") && contents.isEmpty()) { + contents = cba; + } else if (lang == currentLang) { + contents = cba; + break; + } + } + } + + AboutDialog aboutDia(this); + + QByteArray iconArray; + if (!contents.isEmpty()) { + iconArray = helpEngine.aboutIcon(); + QByteArray resources = helpEngine.aboutImages(); + QPixmap pix; + pix.loadFromData(iconArray); + aboutDia.setText(QString::fromUtf8(contents), resources); + if (!pix.isNull()) + aboutDia.setPixmap(pix); + aboutDia.setWindowTitle(aboutDia.documentTitle()); + } else { + QByteArray resources; + aboutDia.setText(tr("<center>" + "<h3>%1</h3>" + "<p>Version %2</p></center>" + "<p>Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).</p>") + .arg(tr("Qt Assistant")).arg(QLatin1String(QT_VERSION_STR)), + resources); + QLatin1String path(":/trolltech/assistant/images/assistant-128.png"); + aboutDia.setPixmap(QString(path)); + } + if (aboutDia.windowTitle().isEmpty()) + aboutDia.setWindowTitle(tr("About %1").arg(windowTitle())); + aboutDia.exec(); +} + +void MainWindow::setContentsVisible(bool visible) +{ + TRACE_OBJ + if (visible) + showContents(); + else + hideContents(); +} + +void MainWindow::showContents() +{ + TRACE_OBJ + activateDockWidget(m_contentWindow); +} + +void MainWindow::hideContents() +{ + TRACE_OBJ + m_contentWindow->parentWidget()->hide(); +} + +void MainWindow::setIndexVisible(bool visible) +{ + TRACE_OBJ + if (visible) + showIndex(); + else + hideIndex(); +} + +void MainWindow::showIndex() +{ + TRACE_OBJ + activateDockWidget(m_indexWindow); +} + +void MainWindow::hideIndex() +{ + TRACE_OBJ + m_indexWindow->parentWidget()->hide(); +} + +void MainWindow::setBookmarksVisible(bool visible) +{ + TRACE_OBJ + if (visible) + showBookmarksDockWidget(); + else + hideBookmarksDockWidget(); +} + +void MainWindow::showBookmarksDockWidget() +{ + TRACE_OBJ + activateDockWidget(m_bookmarkWidget); +} + +void MainWindow::hideBookmarksDockWidget() +{ + TRACE_OBJ + m_bookmarkWidget->parentWidget()->hide(); +} + +void MainWindow::setSearchVisible(bool visible) +{ + TRACE_OBJ + if (visible) + showSearch(); + else + hideSearch(); +} + +void MainWindow::showSearch() +{ + TRACE_OBJ + activateDockWidget(m_searchWindow); +} + +void MainWindow::showOpenPages() +{ + TRACE_OBJ + activateDockWidget(OpenPagesManager::instance()->openPagesWidget()); +} + +void MainWindow::hideSearch() +{ + TRACE_OBJ + m_searchWindow->parentWidget()->hide(); +} + +void MainWindow::activateDockWidget(QWidget *w) +{ + TRACE_OBJ + w->parentWidget()->show(); + w->parentWidget()->raise(); + w->setFocus(); +} + +void MainWindow::setIndexString(const QString &str) +{ + TRACE_OBJ + m_indexWindow->setSearchLineEditText(str); +} + +void MainWindow::activateCurrentBrowser() +{ + TRACE_OBJ + CentralWidget::instance()->activateTab(); +} + +void MainWindow::activateCurrentCentralWidgetTab() +{ + TRACE_OBJ + m_centralWidget->activateTab(); +} + +void MainWindow::updateApplicationFont() +{ + TRACE_OBJ + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + QFont font = qApp->font(); + if (helpEngine.usesAppFont()) + font = helpEngine.appFont(); + + const QWidgetList &widgets = qApp->allWidgets(); + foreach (QWidget* widget, widgets) + widget->setFont(font); +} + +void MainWindow::setupFilterCombo() +{ + TRACE_OBJ + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + QString curFilter = m_filterCombo->currentText(); + if (curFilter.isEmpty()) + curFilter = helpEngine.currentFilter(); + m_filterCombo->clear(); + m_filterCombo->addItems(helpEngine.customFilters()); + int idx = m_filterCombo->findText(curFilter); + if (idx < 0) + idx = 0; + m_filterCombo->setCurrentIndex(idx); +} + +void MainWindow::filterDocumentation(const QString &customFilter) +{ + TRACE_OBJ + HelpEngineWrapper::instance().setCurrentFilter(customFilter); +} + +void MainWindow::expandTOC(int depth) +{ + TRACE_OBJ + Q_ASSERT(depth >= -1); + m_contentWindow->expandToDepth(depth); +} + +void MainWindow::indexingStarted() +{ + TRACE_OBJ + if (!m_progressWidget) { + m_progressWidget = new QWidget(); + QLayout* hlayout = new QHBoxLayout(m_progressWidget); + + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + + QLabel *label = new QLabel(tr("Updating search index")); + label->setSizePolicy(sizePolicy); + hlayout->addWidget(label); + + QProgressBar *progressBar = new QProgressBar(); + progressBar->setRange(0, 0); + progressBar->setTextVisible(false); + progressBar->setSizePolicy(sizePolicy); + + hlayout->setSpacing(6); + hlayout->setMargin(0); + hlayout->addWidget(progressBar); + + statusBar()->addPermanentWidget(m_progressWidget); + } +} + +void MainWindow::indexingFinished() +{ + TRACE_OBJ + statusBar()->removeWidget(m_progressWidget); + delete m_progressWidget; + m_progressWidget = 0; +} + +QString MainWindow::collectionFileDirectory(bool createDir, const QString &cacheDir) +{ + TRACE_OBJ + QString collectionPath = + QDesktopServices::storageLocation(QDesktopServices::DataLocation); + if (collectionPath.isEmpty()) { + if (cacheDir.isEmpty()) + collectionPath = QDir::homePath() + QDir::separator() + + QLatin1String(".assistant"); + else + collectionPath = QDir::homePath() + QLatin1String("/.") + cacheDir; + } else { + if (cacheDir.isEmpty()) + collectionPath = collectionPath + QLatin1String("/Trolltech/Assistant"); + else + collectionPath = collectionPath + QDir::separator() + cacheDir; + } + collectionPath = QDir::cleanPath(collectionPath); + if (createDir) { + QDir dir; + if (!dir.exists(collectionPath)) + dir.mkpath(collectionPath); + } + return collectionPath; +} + +QString MainWindow::defaultHelpCollectionFileName() +{ + TRACE_OBJ + // forces creation of the default collection file path + return collectionFileDirectory(true) + QDir::separator() + + QString(QLatin1String("qthelpcollection_%1.qhc")). + arg(QLatin1String(QT_VERSION_STR)); +} + +void MainWindow::currentFilterChanged(const QString &filter) +{ + TRACE_OBJ + const int index = m_filterCombo->findText(filter); + Q_ASSERT(index != -1); + m_filterCombo->setCurrentIndex(index); +} + +void MainWindow::documentationRemoved(const QString &namespaceName) +{ + TRACE_OBJ + OpenPagesManager::instance()->closePages(namespaceName); +} + +void MainWindow::documentationUpdated(const QString &namespaceName) +{ + TRACE_OBJ + OpenPagesManager::instance()->reloadPages(namespaceName); +} + +void MainWindow::resetQtDocInfo(const QString &component) +{ + TRACE_OBJ + HelpEngineWrapper::instance().setQtDocInfo(component, + QStringList(QDateTime().toString(Qt::ISODate))); +} + +void MainWindow::registerDocumentation(const QString &component, + const QString &absFileName) +{ + TRACE_OBJ + QString ns = QHelpEngineCore::namespaceName(absFileName); + if (ns.isEmpty()) + return; + + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + if (helpEngine.registeredDocumentations().contains(ns)) + helpEngine.unregisterDocumentation(ns); + if (!helpEngine.registerDocumentation(absFileName)) { + QMessageBox::warning(this, tr("Qt Assistant"), + tr("Could not register file '%1': %2"). + arg(absFileName).arg(helpEngine.error())); + } else { + QStringList docInfo; + docInfo << QFileInfo(absFileName).lastModified().toString(Qt::ISODate) + << absFileName; + helpEngine.setQtDocInfo(component, docInfo); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/mainwindow.h b/src/assistant/tools/assistant/mainwindow.h new file mode 100644 index 000000000..6fe10ec45 --- /dev/null +++ b/src/assistant/tools/assistant/mainwindow.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QtCore/QList> +#include <QtCore/QUrl> +#include <QtGui/QMainWindow> + +QT_BEGIN_NAMESPACE + +class QAction; +class QComboBox; +class QFileSystemWatcher; +class QLineEdit; +class QMenu; + +class CentralWidget; +class CmdLineParser; +class ContentWindow; +class IndexWindow; +class OpenPagesWindow; +class QtDocInstaller; +class QHelpEngineCore; +class QHelpEngine; +class SearchWidget; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(CmdLineParser *cmdLine, QWidget *parent = 0); + ~MainWindow(); + + static void activateCurrentBrowser(); + static QString collectionFileDirectory(bool createDir = false, + const QString &cacheDir = QString()); + static QString defaultHelpCollectionFileName(); + +public: + void setIndexString(const QString &str); + void expandTOC(int depth); + bool usesDefaultCollection() const; + +signals: + void initDone(); + +public slots: + void setContentsVisible(bool visible); + void setIndexVisible(bool visible); + void setBookmarksVisible(bool visible); + void setSearchVisible(bool visible); + void syncContents(); + void activateCurrentCentralWidgetTab(); + void currentFilterChanged(const QString &filter); + +private slots: + void showContents(); + void showIndex(); + void showSearch(); + void showOpenPages(); + void insertLastPages(); + void gotoAddress(); + void showPreferences(); + void showNewAddress(); + void showAboutDialog(); + void showNewAddress(const QUrl &url); + void showTopicChooser(const QMap<QString, QUrl> &links, const QString &keyword); + void updateApplicationFont(); + void filterDocumentation(const QString &customFilter); + void setupFilterCombo(); + void lookForNewQtDocumentation(); + void indexingStarted(); + void indexingFinished(); + void qtDocumentationInstalled(); + void registerDocumentation(const QString &component, + const QString &absFileName); + void resetQtDocInfo(const QString &component); + void checkInitState(); + void documentationRemoved(const QString &namespaceName); + void documentationUpdated(const QString &namespaceName); + +private: + bool initHelpDB(bool registerInternalDoc); + void setupActions(); + void closeEvent(QCloseEvent *e); + void activateDockWidget(QWidget *w); + void updateAboutMenuText(); + void setupFilterToolbar(); + void setupAddressToolbar(); + QMenu *toolBarMenu(); + void hideContents(); + void hideIndex(); + void hideSearch(); + +private slots: + void showBookmarksDockWidget(); + void hideBookmarksDockWidget(); + +private: + QWidget *m_bookmarkWidget; + +private: + CentralWidget *m_centralWidget; + IndexWindow *m_indexWindow; + ContentWindow *m_contentWindow; + SearchWidget *m_searchWindow; + QLineEdit *m_addressLineEdit; + QComboBox *m_filterCombo; + + QAction *m_syncAction; + QAction *m_printPreviewAction; + QAction *m_pageSetupAction; + QAction *m_resetZoomAction; + QAction *m_aboutAction; + QAction *m_closeTabAction; + QAction *m_newTabAction; + + QMenu *m_viewMenu; + QMenu *m_toolBarMenu; + + CmdLineParser *m_cmdLine; + + QWidget *m_progressWidget; + QtDocInstaller *m_qtDocInstaller; + + bool m_connectedInitSignals; +}; + +QT_END_NAMESPACE + +#endif // MAINWINDOW_H diff --git a/src/assistant/tools/assistant/openpagesmanager.cpp b/src/assistant/tools/assistant/openpagesmanager.cpp new file mode 100644 index 000000000..272d9e242 --- /dev/null +++ b/src/assistant/tools/assistant/openpagesmanager.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "openpagesmanager.h" + +#include "centralwidget.h" +#include "helpenginewrapper.h" +#include "helpviewer.h" +#include "openpagesmodel.h" +#include "openpagesswitcher.h" +#include "openpageswidget.h" +#include "tracer.h" +#include "../shared/collectionconfiguration.h" + +#include <QtGui/QApplication> +#include <QtGui/QTreeView> + +QT_BEGIN_NAMESPACE + +OpenPagesManager *OpenPagesManager::m_instance = 0; + +OpenPagesManager *OpenPagesManager::createInstance(QObject *parent, + bool defaultCollection, const QUrl &cmdLineUrl) +{ + TRACE_OBJ + Q_ASSERT(!m_instance); + m_instance = new OpenPagesManager(parent, defaultCollection, cmdLineUrl); + return m_instance; +} + +OpenPagesManager *OpenPagesManager::instance() +{ + TRACE_OBJ + Q_ASSERT(m_instance); + return m_instance; +} + +OpenPagesManager::OpenPagesManager(QObject *parent, bool defaultCollection, + const QUrl &cmdLineUrl) + : QObject(parent) + , m_model(new OpenPagesModel(this)) + , m_openPagesWidget(0) + , m_openPagesSwitcher(0) +{ + TRACE_OBJ + m_openPagesWidget = new OpenPagesWidget(m_model); + m_openPagesWidget->setFrameStyle(QFrame::NoFrame); + connect(m_openPagesWidget, SIGNAL(setCurrentPage(QModelIndex)), this, + SLOT(setCurrentPage(QModelIndex))); + connect(m_openPagesWidget, SIGNAL(closePage(QModelIndex)), this, + SLOT(closePage(QModelIndex))); + connect(m_openPagesWidget, SIGNAL(closePagesExcept(QModelIndex)), this, + SLOT(closePagesExcept(QModelIndex))); + + m_openPagesSwitcher = new OpenPagesSwitcher(m_model); + connect(m_openPagesSwitcher, SIGNAL(closePage(QModelIndex)), this, + SLOT(closePage(QModelIndex))); + connect(m_openPagesSwitcher, SIGNAL(setCurrentPage(QModelIndex)), this, + SLOT(setCurrentPage(QModelIndex))); + + setupInitialPages(defaultCollection, cmdLineUrl); +} + +OpenPagesManager ::~OpenPagesManager() +{ + TRACE_OBJ + m_instance = 0; + delete m_openPagesSwitcher; +} + +int OpenPagesManager::pageCount() const +{ + TRACE_OBJ + return m_model->rowCount(); +} + +void OpenPagesManager::setupInitialPages(bool defaultCollection, + const QUrl &cmdLineUrl) +{ + TRACE_OBJ + if (cmdLineUrl.isValid()) { + createPage(cmdLineUrl); + return; + } + + HelpEngineWrapper &helpEngine = HelpEngineWrapper::instance(); + int initialPage = 0; + switch (helpEngine.startOption()) { + case ShowHomePage: + m_model->addPage(helpEngine.homePage()); + break; + case ShowBlankPage: + m_model->addPage(QUrl(QLatin1String("about:blank"))); + break; + case ShowLastPages: { + const QStringList &lastShownPageList = helpEngine.lastShownPages(); + const int pageCount = lastShownPageList.count(); + if (pageCount == 0) { + if (defaultCollection) + m_model->addPage(QUrl(QLatin1String("help"))); + else + m_model->addPage(QUrl(QLatin1String("about:blank"))); + } else { + QStringList zoomFactors = helpEngine.lastZoomFactors(); + while (zoomFactors.count() < pageCount) + zoomFactors.append(CollectionConfiguration::DefaultZoomFactor); + initialPage = helpEngine.lastTabPage(); + if (initialPage >= pageCount) { + qWarning("Initial page set to %d, maximum possible value is %d", + initialPage, pageCount - 1); + initialPage = 0; + } + for (int curPage = 0; curPage < pageCount; ++curPage) { + const QString &curFile = lastShownPageList.at(curPage); + if (helpEngine.findFile(curFile).isValid() + || curFile == QLatin1String("about:blank")) { + m_model->addPage(curFile, zoomFactors.at(curPage).toFloat()); + } else if (curPage <= initialPage && initialPage > 0) + --initialPage; + } + } + break; + } + default: + Q_ASSERT(!"Unhandled option"); + } + + if (m_model->rowCount() == 0) + m_model->addPage(helpEngine.homePage()); + for (int i = 0; i < m_model->rowCount(); ++i) + CentralWidget::instance()->addPage(m_model->pageAt(i)); + setCurrentPage((initialPage >= m_model->rowCount()) + ? m_model->rowCount() - 1 : initialPage); + m_openPagesSwitcher->selectCurrentPage(); +} + +HelpViewer *OpenPagesManager::createPage() +{ + TRACE_OBJ + return createPage(QUrl(QLatin1String("about:blank"))); +} + +void OpenPagesManager::closeCurrentPage() +{ + TRACE_OBJ + Q_ASSERT(m_model->rowCount() > 1); + const QModelIndexList selectedIndexes + = m_openPagesWidget->selectionModel()->selectedRows(); + if (selectedIndexes.isEmpty()) + return; + Q_ASSERT(selectedIndexes.count() == 1); + removePage(selectedIndexes.first().row()); +} + +HelpViewer *OpenPagesManager::createPage(const QUrl &url, bool fromSearch) +{ + TRACE_OBJ + if (HelpViewer::launchWithExternalApp(url)) + return 0; + + m_model->addPage(url); + const int index = m_model->rowCount() - 1; + HelpViewer * const page = m_model->pageAt(index); + CentralWidget::instance()->addPage(page, fromSearch); + setCurrentPage(index); + return page; +} + +HelpViewer *OpenPagesManager::createNewPageFromSearch(const QUrl &url) +{ + TRACE_OBJ + return createPage(url, true); +} + +void OpenPagesManager::closePage(HelpViewer *viewer) +{ + TRACE_OBJ + for (int i = 0; i < m_model->rowCount(); ++i) { + if (m_model->pageAt(i) == viewer) { + removePage(i); + break; + } + } +} + +void OpenPagesManager::closePage(const QModelIndex &index) +{ + TRACE_OBJ + if (index.isValid()) + removePage(index.row()); +} + +void OpenPagesManager::closePages(const QString &nameSpace) +{ + TRACE_OBJ + closeOrReloadPages(nameSpace, false); +} + +void OpenPagesManager::reloadPages(const QString &nameSpace) +{ + TRACE_OBJ + closeOrReloadPages(nameSpace, true); + m_openPagesWidget->selectCurrentPage(); +} + +void OpenPagesManager::closeOrReloadPages(const QString &nameSpace, bool tryReload) +{ + TRACE_OBJ + for (int i = m_model->rowCount() - 1; i >= 0; --i) { + HelpViewer *page = m_model->pageAt(i); + if (page->source().host() != nameSpace) + continue; + if (tryReload && HelpEngineWrapper::instance().findFile(page->source()).isValid()) + page->reload(); + else if (m_model->rowCount() == 1) + page->setSource(QUrl(QLatin1String("about:blank"))); + else + removePage(i); + } +} + +bool OpenPagesManager::pagesOpenForNamespace(const QString &nameSpace) const +{ + TRACE_OBJ + for (int i = 0; i < m_model->rowCount(); ++i) + if (m_model->pageAt(i)->source().host() == nameSpace) + return true; + return false; +} + +void OpenPagesManager::setCurrentPage(const QModelIndex &index) +{ + TRACE_OBJ + if (index.isValid()) + setCurrentPage(index.row()); +} + +void OpenPagesManager::setCurrentPage(int index) +{ + TRACE_OBJ + setCurrentPage(m_model->pageAt(index)); +} + +void OpenPagesManager::setCurrentPage(HelpViewer *page) +{ + TRACE_OBJ + CentralWidget::instance()->setCurrentPage(page); + m_openPagesWidget->selectCurrentPage(); +} + +void OpenPagesManager::removePage(int index) +{ + TRACE_OBJ + CentralWidget::instance()->removePage(index); + m_model->removePage(index); + m_openPagesWidget->selectCurrentPage(); +} + + +void OpenPagesManager::closePagesExcept(const QModelIndex &index) +{ + TRACE_OBJ + if (!index.isValid()) + return; + + int i = 0; + HelpViewer *viewer = m_model->pageAt(index.row()); + while (m_model->rowCount() > 1) { + if (m_model->pageAt(i) != viewer) + removePage(i); + else + ++i; + } +} + +QAbstractItemView *OpenPagesManager::openPagesWidget() const +{ + TRACE_OBJ + return m_openPagesWidget; +} + +void OpenPagesManager::nextPage() +{ + TRACE_OBJ + nextOrPreviousPage(1); +} + +void OpenPagesManager::nextPageWithSwitcher() +{ + TRACE_OBJ + if (!m_openPagesSwitcher->isVisible()) { + m_openPagesSwitcher->selectCurrentPage(); + m_openPagesSwitcher->gotoNextPage(); + showSwitcherOrSelectPage(); + } else { + m_openPagesSwitcher->gotoNextPage(); + } +} + +void OpenPagesManager::previousPage() +{ + TRACE_OBJ + nextOrPreviousPage(-1); +} + +void OpenPagesManager::previousPageWithSwitcher() +{ + TRACE_OBJ + if (!m_openPagesSwitcher->isVisible()) { + m_openPagesSwitcher->selectCurrentPage(); + m_openPagesSwitcher->gotoPreviousPage(); + showSwitcherOrSelectPage(); + } else { + m_openPagesSwitcher->gotoPreviousPage(); + } +} + +void OpenPagesManager::nextOrPreviousPage(int offset) +{ + TRACE_OBJ + setCurrentPage((CentralWidget::instance()->currentIndex() + offset + + m_model->rowCount()) % m_model->rowCount()); +} + +void OpenPagesManager::showSwitcherOrSelectPage() const +{ + TRACE_OBJ + if (QApplication::keyboardModifiers() != Qt::NoModifier) { + const int width = CentralWidget::instance()->width(); + const int height = CentralWidget::instance()->height(); + const QPoint p(CentralWidget::instance()->mapToGlobal(QPoint(0, 0))); + m_openPagesSwitcher->move((width - m_openPagesSwitcher->width()) / 2 + p.x(), + (height - m_openPagesSwitcher->height()) / 2 + p.y()); + m_openPagesSwitcher->setVisible(true); + } else { + m_openPagesSwitcher->selectAndHide(); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/openpagesmanager.h b/src/assistant/tools/assistant/openpagesmanager.h new file mode 100644 index 000000000..c34686c8c --- /dev/null +++ b/src/assistant/tools/assistant/openpagesmanager.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPENPAGESMANAGER_H +#define OPENPAGESMANAGER_H + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QAbstractItemView; +class QModelIndex; +class QUrl; + +class HelpViewer; +class OpenPagesModel; +class OpenPagesSwitcher; +class OpenPagesWidget; + +class OpenPagesManager : public QObject +{ + Q_OBJECT +public: + static OpenPagesManager *createInstance(QObject *parent, + bool defaultCollection, const QUrl &cmdLineUrl); + static OpenPagesManager *instance(); + + bool pagesOpenForNamespace(const QString &nameSpace) const; + void closePages(const QString &nameSpace); + void reloadPages(const QString &nameSpace); + + QAbstractItemView* openPagesWidget() const; + + int pageCount() const; + void setCurrentPage(int index); + +public slots: + HelpViewer *createPage(const QUrl &url, bool fromSearch = false); + HelpViewer *createNewPageFromSearch(const QUrl &url); + HelpViewer *createPage(); + void closeCurrentPage(); + + void nextPage(); + void nextPageWithSwitcher(); + void previousPage(); + void previousPageWithSwitcher(); + + void closePage(HelpViewer *page); + void setCurrentPage(HelpViewer *page); + +private slots: + void setCurrentPage(const QModelIndex &index); + void closePage(const QModelIndex &index); + void closePagesExcept(const QModelIndex &index); + +private: + OpenPagesManager(QObject *parent, bool defaultCollection, + const QUrl &cmdLineUrl); + ~OpenPagesManager(); + + void setupInitialPages(bool defaultCollection, const QUrl &cmdLineUrl); + void closeOrReloadPages(const QString &nameSpace, bool tryReload); + void removePage(int index); + + void nextOrPreviousPage(int offset); + void showSwitcherOrSelectPage() const; + + OpenPagesModel *m_model; + OpenPagesWidget *m_openPagesWidget; + OpenPagesSwitcher *m_openPagesSwitcher; + + static OpenPagesManager *m_instance; +}; + +QT_END_NAMESPACE + +#endif // OPENPAGESMANAGER_H diff --git a/src/assistant/tools/assistant/openpagesmodel.cpp b/src/assistant/tools/assistant/openpagesmodel.cpp new file mode 100644 index 000000000..3517693f9 --- /dev/null +++ b/src/assistant/tools/assistant/openpagesmodel.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "openpagesmodel.h" + +#include "helpenginewrapper.h" +#include "helpviewer.h" +#include "tracer.h" + +#include <QtCore/QStringList> +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +OpenPagesModel::OpenPagesModel(QObject *parent) : QAbstractTableModel(parent) +{ + TRACE_OBJ +} + +int OpenPagesModel::rowCount(const QModelIndex &parent) const +{ + TRACE_OBJ + return parent.isValid() ? 0 : m_pages.count(); +} + +int OpenPagesModel::columnCount(const QModelIndex &/*parent*/) const +{ + TRACE_OBJ + return 2; +} + +QVariant OpenPagesModel::data(const QModelIndex &index, int role) const +{ + TRACE_OBJ + if (!index.isValid() || index.row() >= rowCount() || index.column() > 0 + || role != Qt::DisplayRole) + return QVariant(); + QString title = m_pages.at(index.row())->title(); + title.replace(QLatin1Char('&'), QLatin1String("&&")); + return title.isEmpty() ? QLatin1String("(Untitled)") : title; +} + +void OpenPagesModel::addPage(const QUrl &url, qreal zoom) +{ + TRACE_OBJ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + HelpViewer *page = new HelpViewer(zoom); + connect(page, SIGNAL(titleChanged()), this, SLOT(handleTitleChanged())); + m_pages << page; + endInsertRows(); + page->setSource(url); +} + +void OpenPagesModel::removePage(int index) +{ + TRACE_OBJ + Q_ASSERT(index >= 0 && index < rowCount()); + beginRemoveRows(QModelIndex(), index, index); + HelpViewer *page = m_pages.at(index); + m_pages.removeAt(index); + endRemoveRows(); + page->deleteLater(); +} + +HelpViewer *OpenPagesModel::pageAt(int index) const +{ + TRACE_OBJ + Q_ASSERT(index >= 0 && index < rowCount()); + return m_pages.at(index); +} + +void OpenPagesModel::handleTitleChanged() +{ + TRACE_OBJ + HelpViewer *page = static_cast<HelpViewer *>(sender()); + const int row = m_pages.indexOf(page); + Q_ASSERT(row != -1 ); + const QModelIndex &item = index(row, 0); + emit dataChanged(item, item); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/openpagesmodel.h b/src/assistant/tools/assistant/openpagesmodel.h new file mode 100644 index 000000000..dd28a7c5f --- /dev/null +++ b/src/assistant/tools/assistant/openpagesmodel.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPENPAGESMODEL_H +#define OPENPAGESMODEL_H + +#include <QtCore/QAbstractTableModel> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class HelpViewer; +class QUrl; + +class OpenPagesModel : public QAbstractTableModel +{ + Q_OBJECT +public: + OpenPagesModel(QObject *parent); + + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + void addPage(const QUrl &url, qreal zoom = 0); + void removePage(int index); + HelpViewer *pageAt(int index) const; + +private slots: + void handleTitleChanged(); + +private: + QList<HelpViewer *> m_pages; +}; + +QT_END_NAMESPACE + +#endif // OPENPAGESMODEL_H diff --git a/src/assistant/tools/assistant/openpagesswitcher.cpp b/src/assistant/tools/assistant/openpagesswitcher.cpp new file mode 100644 index 000000000..8e7f29bf0 --- /dev/null +++ b/src/assistant/tools/assistant/openpagesswitcher.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "openpagesswitcher.h" + +#include "centralwidget.h" +#include "openpagesmodel.h" +#include "openpageswidget.h" +#include "tracer.h" + +#include <QtCore/QEvent> + +#include <QtGui/QKeyEvent> +#include <QtGui/QVBoxLayout> + +QT_BEGIN_NAMESPACE + +const int gWidth = 300; +const int gHeight = 200; + +OpenPagesSwitcher::OpenPagesSwitcher(OpenPagesModel *model) + : QFrame(0, Qt::Popup) + , m_openPagesModel(model) +{ + TRACE_OBJ + resize(gWidth, gHeight); + + m_openPagesWidget = new OpenPagesWidget(m_openPagesModel); + + // We disable the frame on this list view and use a QFrame around it instead. + // This improves the look with QGTKStyle. +#ifndef Q_WS_MAC + setFrameStyle(m_openPagesWidget->frameStyle()); +#endif + m_openPagesWidget->setFrameStyle(QFrame::NoFrame); + + m_openPagesWidget->allowContextMenu(false); + m_openPagesWidget->installEventFilter(this); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(m_openPagesWidget); + + connect(m_openPagesWidget, SIGNAL(closePage(QModelIndex)), this, + SIGNAL(closePage(QModelIndex))); + connect(m_openPagesWidget, SIGNAL(setCurrentPage(QModelIndex)), this, + SIGNAL(setCurrentPage(QModelIndex))); +} + +OpenPagesSwitcher::~OpenPagesSwitcher() +{ + TRACE_OBJ +} + +void OpenPagesSwitcher::gotoNextPage() +{ + TRACE_OBJ + selectPageUpDown(1); +} + +void OpenPagesSwitcher::gotoPreviousPage() +{ + TRACE_OBJ + selectPageUpDown(-1); +} + +void OpenPagesSwitcher::selectAndHide() +{ + TRACE_OBJ + setVisible(false); + emit setCurrentPage(m_openPagesWidget->currentIndex()); +} + +void OpenPagesSwitcher::selectCurrentPage() +{ + TRACE_OBJ + m_openPagesWidget->selectCurrentPage(); +} + +void OpenPagesSwitcher::setVisible(bool visible) +{ + TRACE_OBJ + QWidget::setVisible(visible); + if (visible) + setFocus(); +} + +void OpenPagesSwitcher::focusInEvent(QFocusEvent *event) +{ + TRACE_OBJ + Q_UNUSED(event) + m_openPagesWidget->setFocus(); +} + +bool OpenPagesSwitcher::eventFilter(QObject *object, QEvent *event) +{ + TRACE_OBJ + if (object == m_openPagesWidget) { + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + if (ke->key() == Qt::Key_Escape) { + setVisible(false); + return true; + } + + const int key = ke->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter || key == Qt::Key_Space) { + emit setCurrentPage(m_openPagesWidget->currentIndex()); + return true; + } + + Qt::KeyboardModifier modifier = Qt::ControlModifier; +#ifdef Q_WS_MAC + modifier = Qt::AltModifier; +#endif + if (key == Qt::Key_Backtab + && (ke->modifiers() == (modifier | Qt::ShiftModifier))) + gotoPreviousPage(); + else if (key == Qt::Key_Tab && (ke->modifiers() == modifier)) + gotoNextPage(); + } else if (event->type() == QEvent::KeyRelease) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + if (ke->modifiers() == 0 + /*HACK this is to overcome some event inconsistencies between platforms*/ + || (ke->modifiers() == Qt::AltModifier + && (ke->key() == Qt::Key_Alt || ke->key() == -1))) { + selectAndHide(); + } + } + } + return QWidget::eventFilter(object, event); +} + +void OpenPagesSwitcher::selectPageUpDown(int summand) +{ + TRACE_OBJ + const int pageCount = m_openPagesModel->rowCount(); + if (pageCount < 2) + return; + + const QModelIndexList &list = m_openPagesWidget->selectionModel()->selectedIndexes(); + if (list.isEmpty()) + return; + + QModelIndex index = list.first(); + if (!index.isValid()) + return; + + index = m_openPagesModel->index((index.row() + summand + pageCount) % pageCount, 0); + if (index.isValid()) { + m_openPagesWidget->setCurrentIndex(index); + m_openPagesWidget->scrollTo(index, QAbstractItemView::PositionAtCenter); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/openpagesswitcher.h b/src/assistant/tools/assistant/openpagesswitcher.h new file mode 100644 index 000000000..80c7e965d --- /dev/null +++ b/src/assistant/tools/assistant/openpagesswitcher.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPENPAGESSWITCHER_H +#define OPENPAGESSWITCHER_H + +#include <QtGui/QFrame> + +QT_BEGIN_NAMESPACE + +class OpenPagesModel; +class OpenPagesWidget; +class QModelIndex; + +class OpenPagesSwitcher : public QFrame +{ + Q_OBJECT + +public: + OpenPagesSwitcher(OpenPagesModel *model); + ~OpenPagesSwitcher(); + + void gotoNextPage(); + void gotoPreviousPage(); + + void selectAndHide(); + void selectCurrentPage(); + + void setVisible(bool visible); + void focusInEvent(QFocusEvent *event); + bool eventFilter(QObject *object, QEvent *event); + +signals: + void closePage(const QModelIndex &index); + void setCurrentPage(const QModelIndex &index); + +private: + void selectPageUpDown(int summand); + +private: + OpenPagesModel *m_openPagesModel; + OpenPagesWidget *m_openPagesWidget; +}; + +QT_END_NAMESPACE + +#endif // OPENPAGESSWITCHER_H diff --git a/src/assistant/tools/assistant/openpageswidget.cpp b/src/assistant/tools/assistant/openpageswidget.cpp new file mode 100644 index 000000000..db037125b --- /dev/null +++ b/src/assistant/tools/assistant/openpageswidget.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "openpageswidget.h" + +#include "centralwidget.h" +#include "openpagesmodel.h" +#include "tracer.h" + +#include <QtGui/QApplication> +#include <QtGui/QHeaderView> +#include <QtGui/QKeyEvent> +#include <QtGui/QMouseEvent> +#include <QtGui/QMenu> +#include <QtGui/QPainter> + +#ifdef Q_WS_MAC +#include <qmacstyle_mac.h> +#endif + +QT_BEGIN_NAMESPACE + +OpenPagesDelegate::OpenPagesDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ + TRACE_OBJ +} + +void OpenPagesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + TRACE_OBJ + if (option.state & QStyle::State_MouseOver) { + if ((QApplication::mouseButtons() & Qt::LeftButton) == 0) + pressedIndex = QModelIndex(); + QBrush brush = option.palette.alternateBase(); + if (index == pressedIndex) + brush = option.palette.dark(); + painter->fillRect(option.rect, brush); + } + + QStyledItemDelegate::paint(painter, option, index); + + if (index.column() == 1 && index.model()->rowCount() > 1 + && option.state & QStyle::State_MouseOver) { + QIcon icon((option.state & QStyle::State_Selected) + ? ":/trolltech/assistant/images/closebutton.png" + : ":/trolltech/assistant/images/darkclosebutton.png"); + + const QRect iconRect(option.rect.right() - option.rect.height(), + option.rect.top(), option.rect.height(), option.rect.height()); + icon.paint(painter, iconRect, Qt::AlignRight | Qt::AlignVCenter); + } +} + +// -- OpenPagesWidget + +OpenPagesWidget::OpenPagesWidget(OpenPagesModel *model) + : m_allowContextMenu(true) +{ + TRACE_OBJ + setModel(model); + setIndentation(0); + setItemDelegate((m_delegate = new OpenPagesDelegate(this))); + + setTextElideMode(Qt::ElideMiddle); + setAttribute(Qt::WA_MacShowFocusRect, false); + + viewport()->setAttribute(Qt::WA_Hover); + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::SingleSelection); + + header()->hide(); + header()->setStretchLastSection(false); + header()->setResizeMode(0, QHeaderView::Stretch); + header()->setResizeMode(1, QHeaderView::Fixed); + header()->resizeSection(1, 18); + + installEventFilter(this); + setUniformRowHeights(true); + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, SIGNAL(clicked(QModelIndex)), this, + SLOT(handleClicked(QModelIndex))); + connect(this, SIGNAL(pressed(QModelIndex)), this, + SLOT(handlePressed(QModelIndex))); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(contextMenuRequested(QPoint))); +} + +OpenPagesWidget::~OpenPagesWidget() +{ + TRACE_OBJ +} + +void OpenPagesWidget::selectCurrentPage() +{ + TRACE_OBJ + const QModelIndex ¤t = + model()->index(CentralWidget::instance()->currentIndex(), 0); + + QItemSelectionModel * const selModel = selectionModel(); + selModel->select(current, + QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + selModel->clearSelection(); + + setCurrentIndex(current); + scrollTo(currentIndex()); +} + +void OpenPagesWidget::allowContextMenu(bool ok) +{ + TRACE_OBJ + m_allowContextMenu = ok; +} + +void OpenPagesWidget::contextMenuRequested(QPoint pos) +{ + TRACE_OBJ + QModelIndex index = indexAt(pos); + if (!index.isValid() || !m_allowContextMenu) + return; + + if (index.column() == 1) + index = index.sibling(index.row(), 0); + QMenu contextMenu; + QAction *closeEditor = contextMenu.addAction(tr("Close %1").arg(index.data() + .toString())); + QAction *closeOtherEditors = contextMenu.addAction(tr("Close All Except %1") + .arg(index.data().toString())); + + if (model()->rowCount() == 1) { + closeEditor->setEnabled(false); + closeOtherEditors->setEnabled(false); + } + + QAction *action = contextMenu.exec(mapToGlobal(pos)); + if (action == closeEditor) + emit closePage(index); + else if (action == closeOtherEditors) + emit closePagesExcept(index); +} + +void OpenPagesWidget::handlePressed(const QModelIndex &index) +{ + TRACE_OBJ + if (index.column() == 0) + emit setCurrentPage(index); + + if (index.column() == 1) + m_delegate->pressedIndex = index; +} + +void OpenPagesWidget::handleClicked(const QModelIndex &index) +{ + TRACE_OBJ + // implemented here to handle the funky close button and to work around a + // bug in item views where the delegate wouldn't get the QStyle::State_MouseOver + if (index.column() == 1) { + if (model()->rowCount() > 1) + emit closePage(index); + + QWidget *vp = viewport(); + const QPoint &cursorPos = QCursor::pos(); + QMouseEvent e(QEvent::MouseMove, vp->mapFromGlobal(cursorPos), cursorPos, + Qt::NoButton, 0, 0); + QCoreApplication::sendEvent(vp, &e); + } +} + +bool OpenPagesWidget::eventFilter(QObject *obj, QEvent *event) +{ + TRACE_OBJ + if (obj != this) + return QWidget::eventFilter(obj, event); + + if (event->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + if (currentIndex().isValid() && ke->modifiers() == 0) { + const int key = ke->key(); + if (key == Qt::Key_Return || key == Qt::Key_Enter + || key == Qt::Key_Space) { + emit setCurrentPage(currentIndex()); + } else if ((key == Qt::Key_Delete || key == Qt::Key_Backspace) + && model()->rowCount() > 1) { + emit closePage(currentIndex()); + } + } + } else if (event->type() == QEvent::KeyRelease) { + QKeyEvent *ke = static_cast<QKeyEvent*>(event); + if (ke->modifiers() == 0 + && (ke->key() == Qt::Key_Up || ke->key() == Qt::Key_Down)) { + emit setCurrentPage(currentIndex()); + } + } + return QWidget::eventFilter(obj, event); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/openpageswidget.h b/src/assistant/tools/assistant/openpageswidget.h new file mode 100644 index 000000000..6041fe22d --- /dev/null +++ b/src/assistant/tools/assistant/openpageswidget.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Assistant module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPENPAGESWIDGET_H +#define OPENPAGESWIDGET_H + +#include <QtGui/QStyledItemDelegate> +#include <QtGui/QTreeView> + +QT_BEGIN_NAMESPACE + +class OpenPagesModel; + +class OpenPagesDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit OpenPagesDelegate(QObject *parent = 0); + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + mutable QModelIndex pressedIndex; +}; + +class OpenPagesWidget : public QTreeView +{ + Q_OBJECT +public: + OpenPagesWidget(OpenPagesModel *model); + ~OpenPagesWidget(); + + void selectCurrentPage(); + void allowContextMenu(bool ok); + +signals: + void setCurrentPage(const QModelIndex &index); + void closePage(const QModelIndex &index); + void closePagesExcept(const QModelIndex &index); + +private slots: + void contextMenuRequested(QPoint pos); + void handlePressed(const QModelIndex &index); + void handleClicked(const QModelIndex &index); + +private: + bool eventFilter(QObject *obj, QEvent *event); + + bool m_allowContextMenu; + OpenPagesDelegate *m_delegate; +}; + +QT_END_NAMESPACE + +#endif // OPENPAGESWIDGET_H diff --git a/src/assistant/tools/assistant/preferencesdialog.cpp b/src/assistant/tools/assistant/preferencesdialog.cpp new file mode 100644 index 000000000..9bfd35c00 --- /dev/null +++ b/src/assistant/tools/assistant/preferencesdialog.cpp @@ -0,0 +1,507 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "preferencesdialog.h" + +#include "centralwidget.h" +#include "filternamedialog.h" +#include "fontpanel.h" +#include "helpenginewrapper.h" +#include "installdialog.h" +#include "openpagesmanager.h" +#include "tracer.h" + +#include <QtCore/QtAlgorithms> +#include <QtCore/QFileSystemWatcher> + +#include <QtGui/QDesktopWidget> +#include <QtGui/QFileDialog> +#include <QtGui/QFontDatabase> +#include <QtGui/QHeaderView> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> + +#include <QtHelp/QHelpEngineCore> + +QT_BEGIN_NAMESPACE + +PreferencesDialog::PreferencesDialog(QWidget *parent) + : QDialog(parent) + , m_appFontChanged(false) + , m_browserFontChanged(false) + , helpEngine(HelpEngineWrapper::instance()) +{ + TRACE_OBJ + m_ui.setupUi(this); + + connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), + this, SLOT(applyChanges())); + connect(m_ui.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), + this, SLOT(reject())); + + m_hideFiltersTab = !helpEngine.filterFunctionalityEnabled(); + m_hideDocsTab = !helpEngine.documentationManagerEnabled(); + + if (!m_hideFiltersTab) { + 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(); + } else { + m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.filtersTab)); + } + + if (!m_hideDocsTab) { + connect(m_ui.docAddButton, SIGNAL(clicked()), this, + SLOT(addDocumentationLocal())); + connect(m_ui.docRemoveButton, SIGNAL(clicked()), this, + SLOT(removeDocumentation())); + + m_docsBackup = helpEngine.registeredDocumentations(); + m_ui.registeredDocsListWidget->addItems(m_docsBackup); + } else { + m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.docsTab)); + } + + updateFontSettingsPage(); + updateOptionsPage(); + + if (helpEngine.usesAppFont()) + setFont(helpEngine.appFont()); +} + +PreferencesDialog::~PreferencesDialog() +{ + TRACE_OBJ + if (m_appFontChanged) { + helpEngine.setAppFont(m_appFontPanel->selectedFont()); + helpEngine.setUseAppFont(m_appFontPanel->isChecked()); + helpEngine.setAppWritingSystem(m_appFontPanel->writingSystem()); + emit updateApplicationFont(); + } + + if (m_browserFontChanged) { + helpEngine.setBrowserFont(m_browserFontPanel->selectedFont()); + helpEngine.setUseBrowserFont(m_browserFontPanel->isChecked()); + helpEngine.setBrowserWritingSystem(m_browserFontPanel->writingSystem()); + emit updateBrowserFont(); + } + + QString homePage = m_ui.homePageLineEdit->text(); + if (homePage.isEmpty()) + homePage = QLatin1String("help"); + helpEngine.setHomePage(homePage); + + int option = m_ui.helpStartComboBox->currentIndex(); + helpEngine.setStartOption(option); +} + +void PreferencesDialog::showDialog() +{ + TRACE_OBJ + if (exec() != Accepted) + m_appFontChanged = m_browserFontChanged = false; +} + +void PreferencesDialog::updateFilterPage() +{ + TRACE_OBJ + m_ui.filterWidget->clear(); + m_ui.attributeWidget->clear(); + + m_filterMapBackup.clear(); + const QStringList &filters = helpEngine.customFilters(); + foreach (const QString &filter, filters) { + if (filter == HelpEngineWrapper::TrUnfiltered) + continue; + QStringList atts = helpEngine.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, helpEngine.filterAttributes()) + new QTreeWidgetItem(m_ui.attributeWidget, QStringList() << a); + + if (!m_filterMap.keys().isEmpty()) + m_ui.filterWidget->setCurrentRow(0); +} + +void PreferencesDialog::updateAttributes(QListWidgetItem *item) +{ + TRACE_OBJ + 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 PreferencesDialog::updateFilterMap() +{ + TRACE_OBJ + 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 PreferencesDialog::addFilter() +{ + TRACE_OBJ + FilterNameDialog dia(this); + 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 PreferencesDialog::removeFilter() +{ + TRACE_OBJ + 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 PreferencesDialog::addDocumentationLocal() +{ + TRACE_OBJ + const QStringList fileNames = QFileDialog::getOpenFileNames(this, + tr("Add Documentation"), QString(), tr("Qt Compressed Help Files (*.qch)")); + if (fileNames.isEmpty()) + return; + + QStringList invalidFiles; + QStringList alreadyRegistered; + foreach (const QString &fileName, fileNames) { + const QString nameSpace = QHelpEngineCore::namespaceName(fileName); + if (nameSpace.isEmpty()) { + invalidFiles.append(fileName); + continue; + } + + if (m_ui.registeredDocsListWidget->findItems(nameSpace, + Qt::MatchFixedString).count()) { + alreadyRegistered.append(nameSpace); + continue; + } + + if (helpEngine.registerDocumentation(fileName)) { + m_ui.registeredDocsListWidget->addItem(nameSpace); + m_regDocs.append(nameSpace); + m_unregDocs.removeAll(nameSpace); + } + } + + if (!invalidFiles.isEmpty() || !alreadyRegistered.isEmpty()) { + QString message; + if (!alreadyRegistered.isEmpty()) { + foreach (const QString &ns, alreadyRegistered) { + message += tr("The namespace %1 is already registered!") + .arg(QString("<b>%1</b>").arg(ns)) + QLatin1String("<br>"); + } + if (!invalidFiles.isEmpty()) + message.append(QLatin1String("<br>")); + } + + if (!invalidFiles.isEmpty()) { + message += tr("The specified file is not a valid Qt Help File!"); + message.append(QLatin1String("<ul>")); + foreach (const QString &file, invalidFiles) + message += QLatin1String("<li>") + file + QLatin1String("</li>"); + message.append(QLatin1String("</ul>")); + } + QMessageBox::warning(this, tr("Add Documentation"), message); + } + + updateFilterPage(); +} + +void PreferencesDialog::removeDocumentation() +{ + TRACE_OBJ + + bool foundBefore = false; + QList<QListWidgetItem*> l = m_ui.registeredDocsListWidget->selectedItems(); + foreach (QListWidgetItem* item, l) { + const QString& ns = item->text(); + if (!foundBefore && OpenPagesManager::instance()->pagesOpenForNamespace(ns)) { + if (0 == QMessageBox::information(this, tr("Remove Documentation"), + tr("Some documents currently opened in Assistant reference the " + "documentation you are attempting to remove. Removing the " + "documentation will close those documents."), tr("Cancel"), + tr("OK"))) return; + foundBefore = true; + } + + m_unregDocs.append(ns); + delete m_ui.registeredDocsListWidget->takeItem( + m_ui.registeredDocsListWidget->row(item)); + } + + if (m_ui.registeredDocsListWidget->count()) { + m_ui.registeredDocsListWidget->setCurrentRow(0, + QItemSelectionModel::ClearAndSelect); + } +} + +void PreferencesDialog::applyChanges() +{ + TRACE_OBJ + bool filtersWereChanged = false; + if (!m_hideFiltersTab) { + if (m_filterMap.count() != m_filterMapBackup.count()) { + filtersWereChanged = true; + } else { + QMapIterator<QString, QStringList> it(m_filterMapBackup); + while (it.hasNext() && !filtersWereChanged) { + it.next(); + if (!m_filterMap.contains(it.key())) { + filtersWereChanged = true; + } else { + QStringList a = it.value(); + QStringList b = m_filterMap.value(it.key()); + if (a.count() != b.count()) { + filtersWereChanged = true; + } else { + QStringList::const_iterator i(a.constBegin()); + while (i != a.constEnd()) { + if (!b.contains(*i)) { + filtersWereChanged = true; + break; + } + ++i; + } + } + } + } + } + } + + if (filtersWereChanged) { + foreach (const QString &filter, m_removedFilters) + helpEngine.removeCustomFilter(filter); + QMapIterator<QString, QStringList> it(m_filterMap); + while (it.hasNext()) { + it.next(); + helpEngine.addCustomFilter(it.key(), it.value()); + } + } + + foreach (const QString &doc, m_unregDocs) { + OpenPagesManager::instance()->closePages(doc); + helpEngine.unregisterDocumentation(doc); + } + + if (filtersWereChanged || !m_regDocs.isEmpty() || !m_unregDocs.isEmpty()) + helpEngine.setupData(); + + helpEngine.setShowTabs(m_ui.showTabs->isChecked()); + if (m_showTabs != m_ui.showTabs->isChecked()) + emit updateUserInterface(); + + accept(); +} + +void PreferencesDialog::updateFontSettingsPage() +{ + TRACE_OBJ + m_browserFontPanel = new FontPanel(this); + m_browserFontPanel->setCheckable(true); + m_ui.stackedWidget_2->insertWidget(0, m_browserFontPanel); + + m_appFontPanel = new FontPanel(this); + m_appFontPanel->setCheckable(true); + m_ui.stackedWidget_2->insertWidget(1, m_appFontPanel); + + m_ui.stackedWidget_2->setCurrentIndex(0); + + const QString customSettings(tr("Use custom settings")); + m_appFontPanel->setTitle(customSettings); + + QFont font = helpEngine.appFont(); + m_appFontPanel->setSelectedFont(font); + + QFontDatabase::WritingSystem system = helpEngine.appWritingSystem(); + m_appFontPanel->setWritingSystem(system); + + m_appFontPanel->setChecked(helpEngine.usesAppFont()); + + m_browserFontPanel->setTitle(customSettings); + + font = helpEngine.browserFont(); + m_browserFontPanel->setSelectedFont(font); + + system = helpEngine.browserWritingSystem(); + m_browserFontPanel->setWritingSystem(system); + + m_browserFontPanel->setChecked(helpEngine.usesBrowserFont()); + + connect(m_appFontPanel, SIGNAL(toggled(bool)), this, + SLOT(appFontSettingToggled(bool))); + connect(m_browserFontPanel, SIGNAL(toggled(bool)), this, + SLOT(browserFontSettingToggled(bool))); + + QList<QComboBox*> allCombos = m_appFontPanel->findChildren<QComboBox*>(); + foreach (QComboBox* box, allCombos) { + connect(box, SIGNAL(currentIndexChanged(int)), this, + SLOT(appFontSettingChanged(int))); + } + + allCombos = m_browserFontPanel->findChildren<QComboBox*>(); + foreach (QComboBox* box, allCombos) { + connect(box, SIGNAL(currentIndexChanged(int)), this, + SLOT(browserFontSettingChanged(int))); + } +} + +void PreferencesDialog::appFontSettingToggled(bool on) +{ + TRACE_OBJ + Q_UNUSED(on) + m_appFontChanged = true; +} + +void PreferencesDialog::appFontSettingChanged(int index) +{ + TRACE_OBJ + Q_UNUSED(index) + m_appFontChanged = true; +} + +void PreferencesDialog::browserFontSettingToggled(bool on) +{ + TRACE_OBJ + Q_UNUSED(on) + m_browserFontChanged = true; +} + +void PreferencesDialog::browserFontSettingChanged(int index) +{ + TRACE_OBJ + Q_UNUSED(index) + m_browserFontChanged = true; +} + +void PreferencesDialog::updateOptionsPage() +{ + TRACE_OBJ + m_ui.homePageLineEdit->setText(helpEngine.homePage()); + + int option = helpEngine.startOption(); + m_ui.helpStartComboBox->setCurrentIndex(option); + + m_showTabs = helpEngine.showTabs(); + m_ui.showTabs->setChecked(m_showTabs); + + connect(m_ui.blankPageButton, SIGNAL(clicked()), this, SLOT(setBlankPage())); + connect(m_ui.currentPageButton, SIGNAL(clicked()), this, SLOT(setCurrentPage())); + connect(m_ui.defaultPageButton, SIGNAL(clicked()), this, SLOT(setDefaultPage())); +} + +void PreferencesDialog::setBlankPage() +{ + TRACE_OBJ + m_ui.homePageLineEdit->setText(QLatin1String("about:blank")); +} + +void PreferencesDialog::setCurrentPage() +{ + TRACE_OBJ + QString homepage = CentralWidget::instance()->currentSource().toString(); + if (homepage.isEmpty()) + homepage = QLatin1String("help"); + + m_ui.homePageLineEdit->setText(homepage); +} + +void PreferencesDialog::setDefaultPage() +{ + TRACE_OBJ + m_ui.homePageLineEdit->setText(helpEngine.defaultHomePage()); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/preferencesdialog.h b/src/assistant/tools/assistant/preferencesdialog.h new file mode 100644 index 000000000..f9b445e72 --- /dev/null +++ b/src/assistant/tools/assistant/preferencesdialog.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PREFERENCESDIALOG_H +#define PREFERENCESDIALOG_H + +#include <QtGui/QDialog> +#include "ui_preferencesdialog.h" + +QT_BEGIN_NAMESPACE + +class FontPanel; +class HelpEngineWrapper; +class QFileSystemWatcher; + +class PreferencesDialog : public QDialog +{ + Q_OBJECT + +public: + PreferencesDialog(QWidget *parent = 0); + ~PreferencesDialog(); + + void showDialog(); + +private slots: + void updateAttributes(QListWidgetItem *item); + void updateFilterMap(); + void addFilter(); + void removeFilter(); + void addDocumentationLocal(); + void removeDocumentation(); + void applyChanges(); + void appFontSettingToggled(bool on); + void appFontSettingChanged(int index); + void browserFontSettingToggled(bool on); + void browserFontSettingChanged(int index); + + void setBlankPage(); + void setCurrentPage(); + void setDefaultPage(); + +signals: + void updateBrowserFont(); + void updateApplicationFont(); + void updateUserInterface(); + +private: + void updateFilterPage(); + void updateFontSettingsPage(); + void updateOptionsPage(); + + Ui::PreferencesDialogClass m_ui; + bool m_hideFiltersTab; + bool m_hideDocsTab; + QMap<QString, QStringList> m_filterMapBackup; + QMap<QString, QStringList> m_filterMap; + QStringList m_removedFilters; + QStringList m_docsBackup; + QStringList m_regDocs; + QStringList m_unregDocs; + FontPanel *m_appFontPanel; + FontPanel *m_browserFontPanel; + bool m_appFontChanged; + bool m_browserFontChanged; + HelpEngineWrapper &helpEngine; + bool m_showTabs; +}; + +QT_END_NAMESPACE + +#endif // SETTINGSDIALOG_H diff --git a/src/assistant/tools/assistant/preferencesdialog.ui b/src/assistant/tools/assistant/preferencesdialog.ui new file mode 100644 index 000000000..1c6833ad9 --- /dev/null +++ b/src/assistant/tools/assistant/preferencesdialog.ui @@ -0,0 +1,400 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PreferencesDialogClass</class> + <widget class="QDialog" name="PreferencesDialogClass"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>375</width> + <height>275</height> + </rect> + </property> + <property name="windowTitle"> + <string>Preferences</string> + </property> + <layout class="QVBoxLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="fontsTab"> + <attribute name="title"> + <string>Fonts</string> + </attribute> + <layout class="QGridLayout"> + <item row="0" column="0"> + <layout class="QHBoxLayout"> + <item> + <widget class="QLabel" name="fontLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Font settings:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBox"> + <item> + <property name="text"> + <string>Browser</string> + </property> + </item> + <item> + <property name="text"> + <string>Application</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QStackedWidget" name="stackedWidget_2"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="page_4"/> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="filtersTab"> + <attribute name="title"> + <string>Filters</string> + </attribute> + <layout class="QGridLayout"> + <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 row="1" column="2" rowspan="2"> + <widget class="QTreeWidget" name="attributeWidget"> + <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> + <widget class="QWidget" name="docsTab"> + <attribute name="title"> + <string>Documentation</string> + </attribute> + <layout class="QVBoxLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Registered Documentation:</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QListWidget" name="registeredDocsListWidget"> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="docAddButton"> + <property name="text"> + <string>Add...</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="docRemoveButton"> + <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> + <widget class="QWidget" name="optionsTab"> + <attribute name="title"> + <string>Options</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string/> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label_5"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>On help start:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="helpStartComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <item> + <property name="text"> + <string>Show my home page</string> + </property> + </item> + <item> + <property name="text"> + <string>Show a blank page</string> + </property> + </item> + <item> + <property name="text"> + <string>Show my tabs from last session</string> + </property> + </item> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>54</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string/> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Homepage</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="homePageLineEdit"/> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="currentPageButton"> + <property name="text"> + <string>Current Page</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="blankPageButton"> + <property name="text"> + <string>Blank Page</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="defaultPageButton"> + <property name="text"> + <string>Restore to default</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Appearance</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="showTabs"> + <property name="text"> + <string>Show tabs for each individual page</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>72</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11"/> + <resources/> + <connections> + <connection> + <sender>comboBox</sender> + <signal>currentIndexChanged(int)</signal> + <receiver>stackedWidget_2</receiver> + <slot>setCurrentIndex(int)</slot> + <hints> + <hint type="sourcelabel"> + <x>375</x> + <y>32</y> + </hint> + <hint type="destinationlabel"> + <x>347</x> + <y>125</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/assistant/tools/assistant/qtdocinstaller.cpp b/src/assistant/tools/assistant/qtdocinstaller.cpp new file mode 100644 index 000000000..ffe352c87 --- /dev/null +++ b/src/assistant/tools/assistant/qtdocinstaller.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include <QtCore/QDir> +#include <QtCore/QLibraryInfo> +#include <QtCore/QDateTime> +#include <QtCore/QFileSystemWatcher> +#include <QtHelp/QHelpEngineCore> +#include "helpenginewrapper.h" +#include "qtdocinstaller.h" + +QT_BEGIN_NAMESPACE + +QtDocInstaller::QtDocInstaller(const QList<DocInfo> &docInfos) + : m_abort(false), m_docInfos(docInfos) +{ + TRACE_OBJ +} + +QtDocInstaller::~QtDocInstaller() +{ + TRACE_OBJ + if (!isRunning()) + return; + m_mutex.lock(); + m_abort = true; + m_mutex.unlock(); + wait(); +} + +void QtDocInstaller::installDocs() +{ + TRACE_OBJ + start(LowPriority); +} + +void QtDocInstaller::run() +{ + TRACE_OBJ + m_qchDir = QLibraryInfo::location(QLibraryInfo::DocumentationPath) + + QDir::separator() + QLatin1String("qch"); + m_qchFiles = m_qchDir.entryList(QStringList() << QLatin1String("*.qch")); + + bool changes = false; + foreach (const DocInfo &docInfo, m_docInfos) { + changes |= installDoc(docInfo); + m_mutex.lock(); + if (m_abort) { + m_mutex.unlock(); + return; + } + m_mutex.unlock(); + } + emit docsInstalled(changes); +} + +bool QtDocInstaller::installDoc(const DocInfo &docInfo) +{ + TRACE_OBJ + const QString &component = docInfo.first; + const QStringList &info = docInfo.second; + QDateTime dt; + if (!info.isEmpty() && !info.first().isEmpty()) + dt = QDateTime::fromString(info.first(), Qt::ISODate); + + QString qchFile; + if (info.count() == 2) + qchFile = info.last(); + + if (m_qchFiles.isEmpty()) { + emit qchFileNotFound(component); + return false; + } + foreach (const QString &f, m_qchFiles) { + if (f.startsWith(component)) { + QFileInfo fi(m_qchDir.absolutePath() + QDir::separator() + f); + if (dt.isValid() && fi.lastModified().toTime_t() == dt.toTime_t() + && qchFile == fi.absoluteFilePath()) + return false; + emit registerDocumentation(component, fi.absoluteFilePath()); + return true; + } + } + + emit qchFileNotFound(component); + return false; +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/qtdocinstaller.h b/src/assistant/tools/assistant/qtdocinstaller.h new file mode 100644 index 000000000..909df10ce --- /dev/null +++ b/src/assistant/tools/assistant/qtdocinstaller.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTDOCINSTALLER +#define QTDOCINSTALLER + +#include <QtCore/QDir> +#include <QtCore/QMutex> +#include <QtCore/QPair> +#include <QtCore/QStringList> +#include <QtCore/QThread> + +QT_BEGIN_NAMESPACE + +class HelpEngineWrapper; + +class QtDocInstaller : public QThread +{ + Q_OBJECT + +public: + typedef QPair<QString, QStringList> DocInfo; + QtDocInstaller(const QList<DocInfo> &docInfos); + ~QtDocInstaller(); + void installDocs(); + +signals: + void qchFileNotFound(const QString &component); + void registerDocumentation(const QString &component, + const QString &absFileName); + void docsInstalled(bool newDocsInstalled); + +private: + void run(); + bool installDoc(const DocInfo &docInfo); + + bool m_abort; + QMutex m_mutex; + QStringList m_qchFiles; + QDir m_qchDir; + QList<DocInfo> m_docInfos; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/assistant/tools/assistant/remotecontrol.cpp b/src/assistant/tools/assistant/remotecontrol.cpp new file mode 100644 index 000000000..5d93fbc2e --- /dev/null +++ b/src/assistant/tools/assistant/remotecontrol.cpp @@ -0,0 +1,388 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "remotecontrol.h" + +#include "centralwidget.h" +#include "helpenginewrapper.h" +#include "mainwindow.h" +#include "openpagesmanager.h" +#include "tracer.h" + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QFileSystemWatcher> +#include <QtCore/QThread> +#include <QtCore/QTextStream> +#include <QtCore/QSocketNotifier> + +#include <QtGui/QMessageBox> +#include <QtGui/QApplication> + +#include <QtHelp/QHelpEngine> +#include <QtHelp/QHelpIndexWidget> +#include <QtHelp/QHelpSearchQueryWidget> + +#ifdef Q_OS_WIN +# include "remotecontrol_win.h" +#endif + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN + +StdInListenerWin::StdInListenerWin(QObject *parent) + : QThread(parent) +{ + TRACE_OBJ +} + +StdInListenerWin::~StdInListenerWin() +{ + TRACE_OBJ + terminate(); + wait(); +} + +void StdInListenerWin::run() +{ + TRACE_OBJ + bool ok = true; + char chBuf[4096]; + DWORD dwRead; + +#ifndef Q_WS_WINCE + HANDLE hStdin, hStdinDup; + + hStdin = GetStdHandle(STD_INPUT_HANDLE); + if (hStdin == INVALID_HANDLE_VALUE) + return; + + DuplicateHandle(GetCurrentProcess(), hStdin, + GetCurrentProcess(), &hStdinDup, + 0, false, DUPLICATE_SAME_ACCESS); + + CloseHandle(hStdin); +#else + HANDLE hStdinDup; + hStdinDup = stdin; +#endif + + while (ok) { + ok = ReadFile(hStdinDup, chBuf, sizeof(chBuf), &dwRead, NULL); + if (ok && dwRead != 0) + emit receivedCommand(QString::fromLocal8Bit(chBuf, dwRead)); + } +} +#endif + +RemoteControl::RemoteControl(MainWindow *mainWindow) + : QObject(mainWindow) + , m_mainWindow(mainWindow) + , m_debug(false) + , m_caching(true) + , m_syncContents(false) + , m_expandTOC(-2) + , helpEngine(HelpEngineWrapper::instance()) + +{ + TRACE_OBJ + connect(m_mainWindow, SIGNAL(initDone()), this, SLOT(applyCache())); +#ifdef Q_OS_WIN + StdInListenerWin *l = new StdInListenerWin(this); + connect(l, SIGNAL(receivedCommand(QString)), + this, SLOT(handleCommandString(QString))); + l->start(); +#else + QSocketNotifier *notifier = new QSocketNotifier(fileno(stdin), + QSocketNotifier::Read, this); + connect(notifier, SIGNAL(activated(int)), this, SLOT(receivedData())); + notifier->setEnabled(true); +#endif +} + +void RemoteControl::receivedData() +{ + TRACE_OBJ + QByteArray ba; + while (true) { + char c = getc(stdin); + if (c == EOF || c == '\0') + break; + if (c) + ba.append(c); + if (c == '\n') + break; + } + handleCommandString(QString::fromLocal8Bit(ba)); +} + +void RemoteControl::handleCommandString(const QString &cmdString) +{ + TRACE_OBJ + QStringList cmds = cmdString.split(QLatin1Char(';')); + QStringList::const_iterator it = cmds.constBegin(); + while (it != cmds.constEnd()) { + QString cmd, arg; + splitInputString(*it, cmd, arg); + + if (m_debug) + QMessageBox::information(0, tr("Debugging Remote Control"), + tr("Received Command: %1 %2").arg(cmd).arg(arg)); + + if (cmd == QLatin1String("debug")) + handleDebugCommand(arg); + else if (cmd == QLatin1String("show")) + handleShowOrHideCommand(arg, true); + else if (cmd == QLatin1String("hide")) + handleShowOrHideCommand(arg, false); + else if (cmd == QLatin1String("setsource")) + handleSetSourceCommand(arg); + else if (cmd == QLatin1String("synccontents")) + handleSyncContentsCommand(); + else if (cmd == QLatin1String("activatekeyword")) + handleActivateKeywordCommand(arg); + else if (cmd == QLatin1String("activateidentifier")) + handleActivateIdentifierCommand(arg); + else if (cmd == QLatin1String("expandtoc")) + handleExpandTocCommand(arg); + else if (cmd == QLatin1String("setcurrentfilter")) + handleSetCurrentFilterCommand(arg); + else if (cmd == QLatin1String("register")) + handleRegisterCommand(arg); + else if (cmd == QLatin1String("unregister")) + handleUnregisterCommand(arg); + else + return; + + ++it; + } + m_mainWindow->raise(); + m_mainWindow->activateWindow(); +} + +void RemoteControl::splitInputString(const QString &input, QString &cmd, + QString &arg) +{ + TRACE_OBJ + QString cmdLine = input.trimmed(); + int i = cmdLine.indexOf(QLatin1Char(' ')); + cmd = cmdLine.left(i); + arg = cmdLine.mid(i+1); + cmd = cmd.toLower(); +} + +void RemoteControl::handleDebugCommand(const QString &arg) +{ + TRACE_OBJ + m_debug = arg == QLatin1String("on"); +} + +void RemoteControl::handleShowOrHideCommand(const QString &arg, bool show) +{ + TRACE_OBJ + if (arg.toLower() == QLatin1String("contents")) + m_mainWindow->setContentsVisible(show); + else if (arg.toLower() == QLatin1String("index")) + m_mainWindow->setIndexVisible(show); + else if (arg.toLower() == QLatin1String("bookmarks")) + m_mainWindow->setBookmarksVisible(show); + else if (arg.toLower() == QLatin1String("search")) + m_mainWindow->setSearchVisible(show); +} + +void RemoteControl::handleSetSourceCommand(const QString &arg) +{ + TRACE_OBJ + QUrl url(arg); + if (url.isValid()) { + if (url.isRelative()) + url = CentralWidget::instance()->currentSource().resolved(url); + if (m_caching) { + clearCache(); + m_setSource = url; + } else { + CentralWidget::instance()->setSource(url); + } + } +} + +void RemoteControl::handleSyncContentsCommand() +{ + TRACE_OBJ + if (m_caching) + m_syncContents = true; + else + m_mainWindow->syncContents(); +} + +void RemoteControl::handleActivateKeywordCommand(const QString &arg) +{ + TRACE_OBJ + if (m_caching) { + clearCache(); + m_activateKeyword = arg; + } else { + m_mainWindow->setIndexString(arg); + if (!arg.isEmpty()) { + if (!helpEngine.indexWidget()->currentIndex().isValid() + && helpEngine.fullTextSearchFallbackEnabled()) { + if (QHelpSearchEngine *se = helpEngine.searchEngine()) { + m_mainWindow->setSearchVisible(true); + if (QHelpSearchQueryWidget *w = se->queryWidget()) { + w->collapseExtendedSearch(); + QList<QHelpSearchQuery> queryList; + queryList << QHelpSearchQuery(QHelpSearchQuery::DEFAULT, + QStringList(arg)); + w->setQuery(queryList); + se->search(queryList); + } + } + } else { + m_mainWindow->setIndexVisible(true); + helpEngine.indexWidget()->activateCurrentItem(); + } + } + } +} + +void RemoteControl::handleActivateIdentifierCommand(const QString &arg) +{ + TRACE_OBJ + if (m_caching) { + clearCache(); + m_activateIdentifier = arg; + } else { + const QMap<QString, QUrl> &links = helpEngine.linksForIdentifier(arg); + if (!links.isEmpty()) + CentralWidget::instance()->setSource(links.constBegin().value()); + } +} + +void RemoteControl::handleExpandTocCommand(const QString &arg) +{ + TRACE_OBJ + bool ok = false; + int depth = -2; + if (!arg.isEmpty()) + depth = arg.toInt(&ok); + if (!ok || depth < -2) + depth = -2; + + if (m_caching) + m_expandTOC = depth; + else if (depth != -2) + m_mainWindow->expandTOC(depth); +} + +void RemoteControl::handleSetCurrentFilterCommand(const QString &arg) +{ + TRACE_OBJ + if (helpEngine.customFilters().contains(arg)) { + if (m_caching) { + clearCache(); + m_currentFilter = arg; + } else { + helpEngine.setCurrentFilter(arg); + } + } +} + +void RemoteControl::handleRegisterCommand(const QString &arg) +{ + TRACE_OBJ + const QString &absFileName = QFileInfo(arg).absoluteFilePath(); + if (helpEngine.registeredDocumentations(). + contains(QHelpEngineCore::namespaceName(absFileName))) + return; + if (helpEngine.registerDocumentation(absFileName)) + helpEngine.setupData(); +} + +void RemoteControl::handleUnregisterCommand(const QString &arg) +{ + TRACE_OBJ + const QString &absFileName = QFileInfo(arg).absoluteFilePath(); + const QString &ns = QHelpEngineCore::namespaceName(absFileName); + if (helpEngine.registeredDocumentations().contains(ns)) { + OpenPagesManager::instance()->closePages(ns); + if (helpEngine.unregisterDocumentation(ns)) + helpEngine.setupData(); + } +} + +void RemoteControl::applyCache() +{ + TRACE_OBJ + if (m_setSource.isValid()) { + CentralWidget::instance()->setSource(m_setSource); + } else if (!m_activateKeyword.isEmpty()) { + m_mainWindow->setIndexString(m_activateKeyword); + helpEngine.indexWidget()->activateCurrentItem(); + } else if (!m_activateIdentifier.isEmpty()) { + QMap<QString, QUrl> links = + helpEngine.linksForIdentifier(m_activateIdentifier); + if (!links.isEmpty()) + CentralWidget::instance()->setSource(links.constBegin().value()); + } else if (!m_currentFilter.isEmpty()) { + helpEngine.setCurrentFilter(m_currentFilter); + } + + if (m_syncContents) + m_mainWindow->syncContents(); + + Q_ASSERT(m_expandTOC >= -2); + if (m_expandTOC != -2) + m_mainWindow->expandTOC(m_expandTOC); + + m_caching = false; +} + +void RemoteControl::clearCache() +{ + TRACE_OBJ + m_currentFilter.clear(); + m_setSource.clear(); + m_syncContents = false; + m_activateKeyword.clear(); + m_activateIdentifier.clear(); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/remotecontrol.h b/src/assistant/tools/assistant/remotecontrol.h new file mode 100644 index 000000000..4a185f050 --- /dev/null +++ b/src/assistant/tools/assistant/remotecontrol.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REMOTECONTROL_H +#define REMOTECONTROL_H + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +class HelpEngineWrapper; +class MainWindow; + +class RemoteControl : public QObject +{ + Q_OBJECT + +public: + RemoteControl(MainWindow *mainWindow); + +private slots: + void receivedData(); + void handleCommandString(const QString &cmdString); + void applyCache(); + +private: + void clearCache(); + void splitInputString(const QString &input, QString &cmd, QString &arg); + void handleDebugCommand(const QString &arg); + void handleShowOrHideCommand(const QString &arg, bool show); + void handleSetSourceCommand(const QString &arg); + void handleSyncContentsCommand(); + void handleActivateKeywordCommand(const QString &arg); + void handleActivateIdentifierCommand(const QString &arg); + void handleExpandTocCommand(const QString &arg); + void handleSetCurrentFilterCommand(const QString &arg); + void handleRegisterCommand(const QString &arg); + void handleUnregisterCommand(const QString &arg); + +private: + MainWindow *m_mainWindow; + bool m_debug; + + bool m_caching; + QUrl m_setSource; + bool m_syncContents; + QString m_activateKeyword; + QString m_activateIdentifier; + int m_expandTOC; + QString m_currentFilter; + HelpEngineWrapper &helpEngine; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/assistant/tools/assistant/remotecontrol_win.h b/src/assistant/tools/assistant/remotecontrol_win.h new file mode 100644 index 000000000..807815df1 --- /dev/null +++ b/src/assistant/tools/assistant/remotecontrol_win.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REMOTECONTROL_WIN_H +#define REMOTECONTROL_WIN_H + +#include <windows.h> +#include <QtCore/QThread> + +QT_BEGIN_NAMESPACE + +class StdInListenerWin : public QThread +{ + Q_OBJECT + +public: + StdInListenerWin(QObject *parent); + ~StdInListenerWin(); + +signals: + void receivedCommand(const QString &cmd); + +private: + void run(); + bool ok; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/assistant/tools/assistant/searchwidget.cpp b/src/assistant/tools/assistant/searchwidget.cpp new file mode 100644 index 000000000..627d9e745 --- /dev/null +++ b/src/assistant/tools/assistant/searchwidget.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include "mainwindow.h" +#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> + +QT_BEGIN_NAMESPACE + +SearchWidget::SearchWidget(QHelpSearchEngine *engine, QWidget *parent) + : QWidget(parent) + , zoomCount(0) + , searchEngine(engine) +{ + TRACE_OBJ + 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(QUrl)), this, + SIGNAL(requestShowLink(QUrl))); + + connect(searchEngine, SIGNAL(searchingStarted()), this, + SLOT(searchingStarted())); + connect(searchEngine, SIGNAL(searchingFinished(int)), this, + SLOT(searchingFinished(int))); + + QTextBrowser* browser = resultWidget->findChild<QTextBrowser*>(); + if (browser) // Will be null if lib was configured not to use CLucene. + browser->viewport()->installEventFilter(this); +} + +SearchWidget::~SearchWidget() +{ + TRACE_OBJ + // nothing todo +} + +void SearchWidget::zoomIn() +{ + TRACE_OBJ + QTextBrowser* browser = resultWidget->findChild<QTextBrowser*>(); + if (browser && zoomCount != 10) { + zoomCount++; + browser->zoomIn(); + } +} + +void SearchWidget::zoomOut() +{ + TRACE_OBJ + QTextBrowser* browser = resultWidget->findChild<QTextBrowser*>(); + if (browser && zoomCount != -5) { + zoomCount--; + browser->zoomOut(); + } +} + +void SearchWidget::resetZoom() +{ + TRACE_OBJ + if (zoomCount == 0) + return; + + QTextBrowser* browser = resultWidget->findChild<QTextBrowser*>(); + if (browser) { + browser->zoomOut(zoomCount); + zoomCount = 0; + } +} + +void SearchWidget::search() const +{ + TRACE_OBJ + QList<QHelpSearchQuery> query = searchEngine->queryWidget()->query(); + searchEngine->search(query); +} + +void SearchWidget::searchingStarted() +{ + TRACE_OBJ + qApp->setOverrideCursor(QCursor(Qt::WaitCursor)); +} + +void SearchWidget::searchingFinished(int hits) +{ + TRACE_OBJ + Q_UNUSED(hits) + qApp->restoreOverrideCursor(); +} + +bool SearchWidget::eventFilter(QObject* o, QEvent *e) +{ + TRACE_OBJ + QTextBrowser* browser = resultWidget->findChild<QTextBrowser*>(); + if (browser && o == browser->viewport() + && e->type() == QEvent::MouseButtonRelease){ + QMouseEvent *me = static_cast<QMouseEvent*>(e); + QUrl link = resultWidget->linkAt(me->pos()); + if (!link.isEmpty() || link.isValid()) { + bool controlPressed = me->modifiers() & Qt::ControlModifier; + if((me->button() == Qt::LeftButton && controlPressed) + || (me->button() == Qt::MidButton)) { + emit requestShowLinkInNewTab(link); + } + } + } + return QWidget::eventFilter(o,e); +} + +void SearchWidget::keyPressEvent(QKeyEvent *keyEvent) +{ + TRACE_OBJ + if (keyEvent->key() == Qt::Key_Escape) + MainWindow::activateCurrentBrowser(); + else + keyEvent->ignore(); +} + +void SearchWidget::contextMenuEvent(QContextMenuEvent *contextMenuEvent) +{ + TRACE_OBJ + QMenu menu; + QPoint point = contextMenuEvent->globalPos(); + + QTextBrowser* browser = resultWidget->findChild<QTextBrowser*>(); + if (!browser) + return; + + point = browser->mapFromGlobal(point); + if (!browser->rect().contains(point, true)) + return; + + QUrl link = browser->anchorAt(point); + + QKeySequence keySeq(QKeySequence::Copy); + QAction *copyAction = menu.addAction(tr("&Copy") + QLatin1String("\t") + + keySeq.toString(QKeySequence::NativeText)); + copyAction->setEnabled(QTextCursor(browser->textCursor()).hasSelection()); + + QAction *copyAnchorAction = menu.addAction(tr("Copy &Link Location")); + copyAnchorAction->setEnabled(!link.isEmpty() && link.isValid()); + + keySeq = QKeySequence(Qt::CTRL); + QAction *newTabAction = menu.addAction(tr("Open Link in New Tab") + + QLatin1String("\t") + keySeq.toString(QKeySequence::NativeText) + + QLatin1String("LMB")); + newTabAction->setEnabled(!link.isEmpty() && link.isValid()); + + menu.addSeparator(); + + keySeq = QKeySequence::SelectAll; + QAction *selectAllAction = menu.addAction(tr("Select All") + + QLatin1String("\t") + keySeq.toString(QKeySequence::NativeText)); + + 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(); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/searchwidget.h b/src/assistant/tools/assistant/searchwidget.h new file mode 100644 index 000000000..077c78771 --- /dev/null +++ b/src/assistant/tools/assistant/searchwidget.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SEARCHWIDGET_H +#define SEARCHWIDGET_H + +#include <QtCore/QUrl> +#include <QtCore/QPoint> + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QMouseEvent; +class QHelpSearchEngine; +class QHelpSearchResultWidget; + +class SearchWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SearchWidget(QHelpSearchEngine *engine, QWidget *parent = 0); + ~SearchWidget(); + + void zoomIn(); + void zoomOut(); + void resetZoom(); + +signals: + void requestShowLink(const QUrl &url); + void requestShowLinkInNewTab(const QUrl &url); + +private slots: + void search() const; + void searchingStarted(); + void searchingFinished(int hits); + +private: + bool eventFilter(QObject* o, QEvent *e); + void keyPressEvent(QKeyEvent *keyEvent); + void contextMenuEvent(QContextMenuEvent *contextMenuEvent); + +private: + int zoomCount; + QHelpSearchEngine *searchEngine; + QHelpSearchResultWidget *resultWidget; +}; + +QT_END_NAMESPACE + +#endif // SEARCHWIDGET_H diff --git a/src/assistant/tools/assistant/topicchooser.cpp b/src/assistant/tools/assistant/topicchooser.cpp new file mode 100644 index 000000000..6f2810bf4 --- /dev/null +++ b/src/assistant/tools/assistant/topicchooser.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include "topicchooser.h" + +QT_BEGIN_NAMESPACE + +TopicChooser::TopicChooser(QWidget *parent, const QString &keyword, + const QMap<QString, QUrl> &links) + : QDialog(parent) +{ + TRACE_OBJ + ui.setupUi(this); + ui.label->setText(tr("Choose a topic for <b>%1</b>:").arg(keyword)); + + QMap<QString, QUrl>::const_iterator it = links.constBegin(); + for (; it != links.constEnd(); ++it) { + ui.listWidget->addItem(it.key()); + m_links.append(it.value()); + } + + if (ui.listWidget->count() != 0) + ui.listWidget->setCurrentRow(0); + ui.listWidget->setFocus(); + + connect(ui.buttonDisplay, SIGNAL(clicked()), this, SLOT(accept())); + connect(ui.buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(ui.listWidget, SIGNAL(itemActivated(QListWidgetItem*)), this, + SLOT(accept())); +} + +QUrl TopicChooser::link() const +{ + TRACE_OBJ + QListWidgetItem *item = ui.listWidget->currentItem(); + if (!item) + return QUrl(); + + QString title = item->text(); + if (title.isEmpty()) + return QUrl(); + + const int row = ui.listWidget->row(item); + Q_ASSERT(row < m_links.count()); + return m_links.at(row); +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/topicchooser.h b/src/assistant/tools/assistant/topicchooser.h new file mode 100644 index 000000000..a80f1963d --- /dev/null +++ b/src/assistant/tools/assistant/topicchooser.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TOPICCHOOSER_H +#define TOPICCHOOSER_H + +#include "ui_topicchooser.h" + +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QUrl> + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class TopicChooser : public QDialog +{ + Q_OBJECT + +public: + TopicChooser(QWidget *parent, const QString &keyword, + const QMap<QString, QUrl> &links); + + QUrl link() const; + +private: + Ui::TopicChooser ui; + QList<QUrl> m_links; +}; + +QT_END_NAMESPACE + +#endif // TOPICCHOOSER_H diff --git a/src/assistant/tools/assistant/topicchooser.ui b/src/assistant/tools/assistant/topicchooser.ui new file mode 100644 index 000000000..d4c90bb4b --- /dev/null +++ b/src/assistant/tools/assistant/topicchooser.ui @@ -0,0 +1,116 @@ +<UI version="4.0" stdsetdef="1" > + <class>TopicChooser</class> + <widget class="QDialog" name="TopicChooser" > + <property name="objectName" > + <string notr="true">TopicChooser</string> + </property> + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>391</width> + <height>223</height> + </rect> + </property> + <property name="windowTitle" > + <string>Choose Topic</string> + </property> + <property name="sizeGripEnabled" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" > + <property name="objectName" > + <string notr="true">unnamed</string> + </property> + <property name="margin" > + <number>11</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label" > + <property name="objectName" > + <string notr="true">label</string> + </property> + <property name="text" > + <string>&Topics</string> + </property> + <property name="buddy" stdset="0" > + <cstring>listWidget</cstring> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="listWidget" > + <property name="objectName" > + <string notr="true">listWidget</string> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="Layout16" > + <property name="objectName" > + <string notr="true">Layout16</string> + </property> + <layout class="QHBoxLayout" > + <property name="objectName" > + <string notr="true">unnamed</string> + </property> + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer name="Horizontal Spacing2" > + <property name="sizeHint" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + <property name="sizeType" > + <enum>Expanding</enum> + </property> + <property name="orientation" > + <enum>Horizontal</enum> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="buttonDisplay" > + <property name="objectName" > + <string notr="true">buttonDisplay</string> + </property> + <property name="text" > + <string>&Display</string> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + <property name="default" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="buttonCancel" > + <property name="objectName" > + <string notr="true">buttonCancel</string> + </property> + <property name="text" > + <string>&Close</string> + </property> + <property name="autoDefault" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> +</UI> diff --git a/src/assistant/tools/assistant/tracer.h b/src/assistant/tools/assistant/tracer.h new file mode 100644 index 000000000..1ed609acb --- /dev/null +++ b/src/assistant/tools/assistant/tracer.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TRACER_H +#define TRACER_H + +#include <QtCore/QtGlobal> + +QT_BEGIN_NAMESPACE + +class Tracer +{ +public: + Tracer(const char *func) : m_func(func) + { + qDebug("Entering function %s.", m_func); + } + + ~Tracer() + { + qDebug("Leaving function %s.", m_func); + } + +private: + const char * const m_func; +}; + +QT_END_NAMESPACE + +// #define TRACING_REQUESTED +#ifdef TRACING_REQUESTED +#define TRACE_OBJ Tracer traceObj__(Q_FUNC_INFO); +#else +#define TRACE_OBJ +#endif + +#endif // TRACER_H diff --git a/src/assistant/tools/assistant/xbelsupport.cpp b/src/assistant/tools/assistant/xbelsupport.cpp new file mode 100644 index 000000000..2979d10c1 --- /dev/null +++ b/src/assistant/tools/assistant/xbelsupport.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tracer.h" + +#include "xbelsupport.h" + +#include "bookmarkitem.h" +#include "bookmarkmodel.h" + +#include <QtCore/QDate> +#include <QtCore/QModelIndex> + +QT_BEGIN_NAMESPACE + +struct Bookmark { + QString title; + QString url; + bool folded; +}; + +XbelWriter::XbelWriter(BookmarkModel *model) + : QXmlStreamWriter() + , bookmarkModel(model) +{ + TRACE_OBJ + setAutoFormatting(true); +} + +void XbelWriter::writeToFile(QIODevice *device) +{ + TRACE_OBJ + setDevice(device); + + writeStartDocument(); + writeDTD(QLatin1String("<!DOCTYPE xbel>")); + writeStartElement(QLatin1String("xbel")); + writeAttribute(QLatin1String("version"), QLatin1String("1.0")); + + const QModelIndex &root = bookmarkModel->index(0,0, QModelIndex()); + for (int i = 0; i < bookmarkModel->rowCount(root); ++i) + writeData(bookmarkModel->index(i, 0, root)); + writeEndDocument(); +} + +void XbelWriter::writeData(const QModelIndex &index) +{ + TRACE_OBJ + if (index.isValid()) { + Bookmark entry; + entry.title = index.data().toString(); + entry.url = index.data(UserRoleUrl).toString(); + + if (index.data(UserRoleFolder).toBool()) { + writeStartElement(QLatin1String("folder")); + entry.folded = !index.data(UserRoleExpanded).toBool(); + writeAttribute(QLatin1String("folded"), entry.folded + ? QLatin1String("yes") : QLatin1String("no")); + writeTextElement(QLatin1String("title"), entry.title); + + for (int i = 0; i < bookmarkModel->rowCount(index); ++i) + writeData(bookmarkModel->index(i, 0 , index)); + writeEndElement(); + } else { + writeStartElement(QLatin1String("bookmark")); + writeAttribute(QLatin1String("href"), entry.url); + writeTextElement(QLatin1String("title"), entry.title); + writeEndElement(); + } + } +} + +// -- XbelReader + +XbelReader::XbelReader(BookmarkModel *model) + : QXmlStreamReader() + , bookmarkModel(model) +{ + TRACE_OBJ +} + +bool XbelReader::readFromFile(QIODevice *device) +{ + TRACE_OBJ + setDevice(device); + + while (!atEnd()) { + readNext(); + + if (isStartElement()) { + if (name() == QLatin1String("xbel") + && attributes().value(QLatin1String("version")) + == QLatin1String("1.0")) { + const QModelIndex &root = bookmarkModel->index(0,0, QModelIndex()); + parents.append(bookmarkModel->addItem(root, true)); + readXBEL(); + bookmarkModel->setData(parents.first(), + QDate::currentDate().toString(Qt::ISODate), Qt::EditRole); + } else { + raiseError(QLatin1String("The file is not an XBEL version 1.0 file.")); + } + } + } + + return !error(); +} + +void XbelReader::readXBEL() +{ + TRACE_OBJ + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == QLatin1String("folder")) + readFolder(); + else if (name() == QLatin1String("bookmark")) + readBookmark(); + else + readUnknownElement(); + } + } +} + +void XbelReader::readFolder() +{ + TRACE_OBJ + parents.append(bookmarkModel->addItem(parents.last(), true)); + bookmarkModel->setData(parents.last(), + attributes().value(QLatin1String("folded")) == QLatin1String("no"), + UserRoleExpanded); + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == QLatin1String("title")) { + bookmarkModel->setData(parents.last(), readElementText(), + Qt::EditRole); + } else if (name() == QLatin1String("folder")) + readFolder(); + else if (name() == QLatin1String("bookmark")) + readBookmark(); + else + readUnknownElement(); + } + } + + parents.removeLast(); +} + +void XbelReader::readBookmark() +{ + TRACE_OBJ + const QModelIndex &index = bookmarkModel->addItem(parents.last(), false); + if (BookmarkItem* item = bookmarkModel->itemFromIndex(index)) { + item->setData(UserRoleUrl, attributes().value(QLatin1String("href")) + .toString()); + } + + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) { + if (name() == QLatin1String("title")) + bookmarkModel->setData(index, readElementText(), Qt::EditRole); + else + readUnknownElement(); + } + } +} + +void XbelReader::readUnknownElement() +{ + TRACE_OBJ + while (!atEnd()) { + readNext(); + + if (isEndElement()) + break; + + if (isStartElement()) + readUnknownElement(); + } +} + +QT_END_NAMESPACE diff --git a/src/assistant/tools/assistant/xbelsupport.h b/src/assistant/tools/assistant/xbelsupport.h new file mode 100644 index 000000000..4c2a211ad --- /dev/null +++ b/src/assistant/tools/assistant/xbelsupport.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Assistant of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XBELSUPPORT_H +#define XBELSUPPORT_H + +#include <QtXml/QXmlStreamReader> +#include <QtCore/QPersistentModelIndex> + +QT_FORWARD_DECLARE_CLASS(QIODevice) +QT_FORWARD_DECLARE_CLASS(QModelIndex) + +QT_BEGIN_NAMESPACE + +class BookmarkModel; + +class XbelWriter : public QXmlStreamWriter +{ +public: + XbelWriter(BookmarkModel *model); + void writeToFile(QIODevice *device); + +private: + void writeData(const QModelIndex &index); + +private: + BookmarkModel *bookmarkModel; +}; + +class XbelReader : public QXmlStreamReader +{ +public: + XbelReader(BookmarkModel *model); + bool readFromFile(QIODevice *device); + +private: + void readXBEL(); + void readFolder(); + void readBookmark(); + void readUnknownElement(); + +private: + BookmarkModel *bookmarkModel; + QList<QPersistentModelIndex> parents; +}; + +QT_END_NAMESPACE + +#endif // XBELSUPPORT_H |