mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
297 lines
10 KiB
C++
297 lines
10 KiB
C++
![]() |
/* This file is part of KDevelop
|
||
|
Copyright 2009 Aleix Pol <aleixpol@kde.org>
|
||
|
Copyright 2009 David Nolden <david.nolden.kdevelop@art-master.de>
|
||
|
Copyright 2010 Benjamin Port <port.benjamin@gmail.com>
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Library General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2 of the License, or (at your option) any later version.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Library General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "qthelpdocumentation.h"
|
||
|
#include <QLabel>
|
||
|
#include <KLocale>
|
||
|
#include <KIcon>
|
||
|
#include <QTreeView>
|
||
|
#include <QHelpContentModel>
|
||
|
#include <QHeaderView>
|
||
|
#include <QMenu>
|
||
|
#include <interfaces/icore.h>
|
||
|
#include <interfaces/idocumentationcontroller.h>
|
||
|
#include <documentation/standarddocumentationview.h>
|
||
|
#include "qthelpnetwork.h"
|
||
|
#include "qthelpproviderabstract.h"
|
||
|
#include "kdebug.h"
|
||
|
#include <QTemporaryFile>
|
||
|
QtHelpProviderAbstract* QtHelpDocumentation::s_provider=0;
|
||
|
|
||
|
QtHelpDocumentation::QtHelpDocumentation(const QString& name, const QMap<QString, QUrl>& info)
|
||
|
: m_provider(s_provider), m_name(name), m_info(info), m_current(info.constBegin()), lastView(0)
|
||
|
{}
|
||
|
|
||
|
QtHelpDocumentation::QtHelpDocumentation(const QString& name, const QMap<QString, QUrl>& info, const QString& key)
|
||
|
: m_provider(s_provider), m_name(name), m_info(info), m_current(m_info.find(key)), lastView(0)
|
||
|
{ Q_ASSERT(m_current!=m_info.constEnd()); }
|
||
|
|
||
|
QString QtHelpDocumentation::description() const
|
||
|
{
|
||
|
QUrl url(m_current.value());
|
||
|
QByteArray data = m_provider->engine()->fileData(url);
|
||
|
|
||
|
//Extract a short description from the html data
|
||
|
QString dataString = QString::fromLatin1(data); ///@todo encoding
|
||
|
QString fragment = url.fragment();
|
||
|
|
||
|
QString p = "((\\\")|(\\\'))";
|
||
|
QString exp = "< a name = " + p + fragment + p + " > < / a >";
|
||
|
QString optionalSpace = "( )*";
|
||
|
exp.replace(' ', optionalSpace);
|
||
|
QRegExp findFragment(exp);
|
||
|
int pos = findFragment.indexIn(dataString);
|
||
|
if(fragment.isEmpty()) {
|
||
|
pos = 0;
|
||
|
}else{
|
||
|
//Check if there is a title opening-tag right before the fragment, and if yes add it, so we have a nicely formatted caption
|
||
|
QString titleRegExp("< h\\d class = \".*\" >");
|
||
|
titleRegExp.replace(" ", optionalSpace);
|
||
|
QRegExp findTitle(titleRegExp);
|
||
|
int titleStart = findTitle.lastIndexIn(dataString, pos);
|
||
|
int titleEnd = titleStart + findTitle.matchedLength();
|
||
|
if(titleStart != -1) {
|
||
|
QString between = dataString.mid(titleEnd, pos-titleEnd).trimmed();
|
||
|
// if(between.isEmpty())
|
||
|
pos = titleStart;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(pos != -1) {
|
||
|
|
||
|
QString exp = "< a name = " + p + "((\\S)*)" + p + " > < / a >";
|
||
|
exp.replace(" ", optionalSpace);
|
||
|
QRegExp nextFragmentExpression(exp);
|
||
|
int endPos = nextFragmentExpression.indexIn(dataString, pos+(fragment.size() ? findFragment.matchedLength() : 0));
|
||
|
if(endPos == -1)
|
||
|
endPos = dataString.size();
|
||
|
|
||
|
{
|
||
|
//Find the end of the last paragraph or newline, so we don't add prefixes of the following fragment
|
||
|
QString newLineRegExp("< br / > | < / p >");
|
||
|
newLineRegExp.replace(" ", optionalSpace);
|
||
|
QRegExp lastNewLine(newLineRegExp);
|
||
|
int newEnd = dataString.lastIndexOf(lastNewLine, endPos);
|
||
|
if(newEnd != -1 && newEnd > pos)
|
||
|
endPos = newEnd + lastNewLine.matchedLength();
|
||
|
}
|
||
|
|
||
|
{
|
||
|
//Find the title, and start from there
|
||
|
QString titleRegExp("< h\\d class = \"title\" >");
|
||
|
titleRegExp.replace(" ", optionalSpace);
|
||
|
QRegExp findTitle(titleRegExp);
|
||
|
int idx = findTitle.indexIn(dataString);
|
||
|
if(idx > pos && idx < endPos)
|
||
|
pos = idx;
|
||
|
}
|
||
|
|
||
|
|
||
|
QString thisFragment = dataString.mid(pos, endPos - pos);
|
||
|
|
||
|
{
|
||
|
//Completely remove the first large header found, since we don't need a header
|
||
|
QString headerRegExp("< h\\d.*>.*< / h\\d >");
|
||
|
headerRegExp.replace(" ", optionalSpace);
|
||
|
QRegExp findHeader(headerRegExp);
|
||
|
findHeader.setMinimal(true);
|
||
|
int idx = findHeader.indexIn(thisFragment);
|
||
|
if(idx != -1) {
|
||
|
thisFragment.remove(idx, findHeader.matchedLength());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
//Replace all gigantic header-font sizes with <big>
|
||
|
|
||
|
{
|
||
|
QString sizeRegExp("< h\\d ");
|
||
|
sizeRegExp.replace(" ", optionalSpace);
|
||
|
QRegExp findSize(sizeRegExp);
|
||
|
thisFragment.replace(findSize, "<big ");
|
||
|
}
|
||
|
{
|
||
|
QString sizeCloseRegExp("< / h\\d >");
|
||
|
sizeCloseRegExp.replace(" ", optionalSpace);
|
||
|
QRegExp closeSize(sizeCloseRegExp);
|
||
|
thisFragment.replace(closeSize, "</big><br />");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
{
|
||
|
//Replace paragraphs by newlines
|
||
|
|
||
|
QString begin("< p >");
|
||
|
begin.replace(" ", optionalSpace);
|
||
|
|
||
|
QRegExp findBegin(begin);
|
||
|
thisFragment.replace(findBegin, "");
|
||
|
|
||
|
QString end("< /p >");
|
||
|
end.replace(" ", optionalSpace);
|
||
|
|
||
|
QRegExp findEnd(end);
|
||
|
thisFragment.replace(findEnd, "<br />");
|
||
|
}
|
||
|
|
||
|
{
|
||
|
//Remove links, because they won't work
|
||
|
QString link("< a href = " + p + ".*" + p);
|
||
|
link.replace(" ", optionalSpace);
|
||
|
QRegExp exp(link, Qt::CaseSensitive);
|
||
|
exp.setMinimal(true);
|
||
|
thisFragment.replace(exp, "<a ");
|
||
|
}
|
||
|
|
||
|
return thisFragment;
|
||
|
}
|
||
|
|
||
|
return QStringList(m_info.keys()).join(", ");
|
||
|
}
|
||
|
|
||
|
void QtHelpDocumentation::setUserStyleSheet(QWebView* view, const QUrl& url)
|
||
|
{
|
||
|
|
||
|
QTemporaryFile* file = new QTemporaryFile(view);
|
||
|
file->open();
|
||
|
|
||
|
QTextStream ts(file);
|
||
|
ts << "html { background: white !important; }\n";
|
||
|
if (url.scheme() == "qthelp" && url.host().startsWith("com.trolltech.qt.")) {
|
||
|
ts << ".content .toc + .title + p { clear:left; }\n"
|
||
|
<< "#qtdocheader .qtref { position: absolute !important; top: 5px !important; right: 0 !important; }\n";
|
||
|
}
|
||
|
file->close();
|
||
|
view->settings()->setUserStyleSheetUrl(KUrl(file->fileName()));
|
||
|
|
||
|
delete m_lastStyleSheet.data();
|
||
|
m_lastStyleSheet = file;
|
||
|
}
|
||
|
|
||
|
QWidget* QtHelpDocumentation::documentationWidget(KDevelop::DocumentationFindWidget* findWidget, QWidget* parent)
|
||
|
{
|
||
|
QWidget* ret;
|
||
|
if(m_info.isEmpty()) { //QtHelp sometimes has empty info maps. e.g. availableaudioeffects i 4.5.2
|
||
|
ret=new QLabel(i18n("Could not find any documentation for '%1'", m_name), parent);
|
||
|
} else {
|
||
|
KDevelop::StandardDocumentationView* view=new KDevelop::StandardDocumentationView(findWidget, parent);
|
||
|
view->page()->setNetworkAccessManager(new HelpNetworkAccessManager(m_provider->engine(), 0));
|
||
|
view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
|
||
|
view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||
|
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(viewContextMenuRequested(QPoint)));
|
||
|
|
||
|
QObject::connect(view, SIGNAL(linkClicked(QUrl)), SLOT(jumpedTo(QUrl)));
|
||
|
|
||
|
setUserStyleSheet(view, m_current.value());
|
||
|
view->load(m_current.value());
|
||
|
ret=view;
|
||
|
lastView=view;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void QtHelpDocumentation::viewContextMenuRequested(const QPoint& pos)
|
||
|
{
|
||
|
KDevelop::StandardDocumentationView* view = qobject_cast<KDevelop::StandardDocumentationView*>(sender());
|
||
|
if (!view)
|
||
|
return;
|
||
|
|
||
|
QMenu menu;
|
||
|
QAction* copyAction = view->pageAction(QWebPage::Copy);
|
||
|
copyAction->setIcon(KIcon("edit-copy"));
|
||
|
menu.addAction(copyAction);
|
||
|
|
||
|
if (m_info.count() > 1) {
|
||
|
menu.addSeparator();
|
||
|
|
||
|
QActionGroup* actionGroup = new QActionGroup(&menu);
|
||
|
foreach(const QString& name, m_info.keys()) {
|
||
|
QtHelpAlternativeLink* act=new QtHelpAlternativeLink(name, this, actionGroup);
|
||
|
act->setCheckable(true);
|
||
|
act->setChecked(name==m_current.key());
|
||
|
menu.addAction(act);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
menu.exec(view->mapToGlobal(pos));
|
||
|
}
|
||
|
|
||
|
|
||
|
void QtHelpDocumentation::jumpedTo(const QUrl& newUrl)
|
||
|
{
|
||
|
Q_ASSERT(lastView);
|
||
|
m_provider->jumpedTo(newUrl);
|
||
|
setUserStyleSheet(lastView, newUrl);
|
||
|
lastView->load(newUrl);
|
||
|
}
|
||
|
|
||
|
KDevelop::IDocumentationProvider* QtHelpDocumentation::provider() const
|
||
|
{
|
||
|
return m_provider;
|
||
|
}
|
||
|
|
||
|
QtHelpAlternativeLink::QtHelpAlternativeLink(const QString& name, const QtHelpDocumentation* doc, QObject* parent)
|
||
|
: QAction(name, parent), mDoc(doc), mName(name)
|
||
|
{
|
||
|
connect(this, SIGNAL(triggered()), SLOT(showUrl()));
|
||
|
}
|
||
|
|
||
|
void QtHelpAlternativeLink::showUrl()
|
||
|
{
|
||
|
KSharedPtr<KDevelop::IDocumentation> newDoc(new QtHelpDocumentation(mName, mDoc->info(), mName));
|
||
|
KDevelop::ICore::self()->documentationController()->showDocumentation(newDoc);
|
||
|
}
|
||
|
|
||
|
HomeDocumentation::HomeDocumentation() : m_provider(QtHelpDocumentation::s_provider)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
QWidget* HomeDocumentation::documentationWidget(KDevelop::DocumentationFindWidget*, QWidget* parent)
|
||
|
{
|
||
|
QTreeView* w=new QTreeView(parent);
|
||
|
w->header()->setVisible(false);
|
||
|
w->setModel(m_provider->engine()->contentModel());
|
||
|
|
||
|
connect(w, SIGNAL(clicked(QModelIndex)), SLOT(clicked(QModelIndex)));
|
||
|
return w;
|
||
|
}
|
||
|
|
||
|
void HomeDocumentation::clicked(const QModelIndex& idx)
|
||
|
{
|
||
|
QHelpContentModel* model = m_provider->engine()->contentModel();
|
||
|
QHelpContentItem* it=model->contentItemAt(idx);
|
||
|
QMap<QString, QUrl> info;
|
||
|
info.insert(it->title(), it->url());
|
||
|
|
||
|
KSharedPtr<KDevelop::IDocumentation> newDoc(new QtHelpDocumentation(it->title(), info));
|
||
|
KDevelop::ICore::self()->documentationController()->showDocumentation(newDoc);
|
||
|
}
|
||
|
|
||
|
QString HomeDocumentation::name() const
|
||
|
{
|
||
|
return i18n("QtHelp Home Page");
|
||
|
}
|
||
|
|
||
|
KDevelop::IDocumentationProvider* HomeDocumentation::provider() const
|
||
|
{
|
||
|
return m_provider;
|
||
|
}
|