Files
create/src/Mod/Web/Gui/BrowserView.cpp

522 lines
16 KiB
C++

/***************************************************************************
* Copyright (c) 2009 Jürgen Riegel <juergen.riegel@web.de> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* 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., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <QAbstractTextDocumentLayout>
# include <QApplication>
# include <QClipboard>
# include <QDateTime>
# include <QHBoxLayout>
# include <QMessageBox>
# include <QNetworkRequest>
# include <QPainter>
# include <QPrinter>
# include <QPrintDialog>
# include <QScrollBar>
# include <QMouseEvent>
# if QT_VERSION >= 0x040400
# include <QWebFrame>
# include <QWebView>
# include <QWebSettings>
# endif
# include <QStatusBar>
# include <QTextBlock>
# include <QTextCodec>
# include <QTextStream>
# include <QTimer>
# include <QFileInfo>
# include <QDesktopServices>
# include <QMenu>
# include <QDesktopWidget>
# include <QSignalMapper>
# include <QPointer>
#endif
#include "BrowserView.h"
#include "CookieJar.h"
#include <Gui/Application.h>
#include <Gui/MainWindow.h>
#include <Gui/ProgressBar.h>
#include <Gui/Command.h>
#include <Gui/OnlineDocumentation.h>
#include <Gui/DownloadManager.h>
#include <Base/Parameter.h>
#include <Base/Exception.h>
#include <CXX/Extensions.hxx>
using namespace WebGui;
using namespace Gui;
namespace WebGui {
class BrowserViewPy : public Py::PythonExtension<BrowserViewPy>
{
public:
static void init_type(void); // announce properties and methods
BrowserViewPy(BrowserView* view);
~BrowserViewPy();
Py::Object repr();
Py::Object setHtml(const Py::Tuple&);
private:
QPointer<BrowserView> myWebView;
};
void BrowserViewPy::init_type()
{
behaviors().name("BrowserView");
behaviors().doc("Python interface class to BrowserView");
// you must have overwritten the virtual functions
behaviors().supportRepr();
behaviors().supportGetattr();
behaviors().supportSetattr();
behaviors().readyType();
add_varargs_method("setHtml",&BrowserViewPy::setHtml,"setHtml(str)");
}
BrowserViewPy::BrowserViewPy(BrowserView* view) : myWebView(view)
{
}
BrowserViewPy::~BrowserViewPy()
{
}
Py::Object BrowserViewPy::repr()
{
std::stringstream s;
s << "<BrowserView at " << this << ">";
return Py::String(s.str());
}
Py::Object BrowserViewPy::setHtml(const Py::Tuple& args)
{
char* HtmlCode;
char* BaseUrl;
if (!PyArg_ParseTuple(args.ptr(), "et|s","utf-8",&HtmlCode,&BaseUrl))
throw Py::Exception();
std::string EncodedHtml = std::string(HtmlCode);
PyMem_Free(HtmlCode);
if (myWebView)
myWebView->setHtml(QString::fromUtf8(EncodedHtml.c_str()), QUrl(QString::fromLatin1(BaseUrl)));
return Py::None();
}
}
/**
* Constructs a WebView widget which can be zoomed with Ctrl+Mousewheel
*
*/
WebView::WebView(QWidget *parent)
: QWebView(parent)
{
// Increase html font size for high DPI displays
QRect mainScreenSize = QApplication::desktop()->screenGeometry();
if (mainScreenSize.width() > 1920){
setTextSizeMultiplier (mainScreenSize.width()/1920.0);
}
}
void WebView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::MidButton) {
QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos());
if (!r.linkUrl().isEmpty()) {
openLinkInNewWindow(r.linkUrl());
return;
}
}
QWebView::mousePressEvent(event);
}
void WebView::wheelEvent(QWheelEvent *event)
{
if (QApplication::keyboardModifiers() & Qt::ControlModifier) {
qreal factor = zoomFactor() + (-event->delta() / 800.0);
setZoomFactor(factor);
event->accept();
return;
}
QWebView::wheelEvent(event);
}
void WebView::contextMenuEvent(QContextMenuEvent *event)
{
QWebHitTestResult r = page()->mainFrame()->hitTestContent(event->pos());
if (!r.linkUrl().isEmpty()) {
QMenu menu(this);
menu.addAction(pageAction(QWebPage::OpenLink));
// building a custom signal for external browser action
QSignalMapper* signalMapper = new QSignalMapper (this);
signalMapper->setProperty("url", QVariant(r.linkUrl()));
connect(signalMapper, SIGNAL(mapped(int)),
this, SLOT(triggerContextMenuAction(int)));
QAction* extAction = menu.addAction(tr("Open in External Browser"));
connect (extAction, SIGNAL(triggered()), signalMapper, SLOT(map()));
signalMapper->setMapping(extAction, QWebPage::OpenLink);
QAction* newAction = menu.addAction(tr("Open in new window"));
connect (newAction, SIGNAL(triggered()), signalMapper, SLOT(map()));
signalMapper->setMapping(newAction, QWebPage::OpenLinkInNewWindow);
menu.addAction(pageAction(QWebPage::DownloadLinkToDisk));
menu.addAction(pageAction(QWebPage::CopyLinkToClipboard));
menu.exec(mapToGlobal(event->pos()));
return;
}
QWebView::contextMenuEvent(event);
}
void WebView::triggerContextMenuAction(int id)
{
QObject* s = sender();
QUrl url = s->property("url").toUrl();
switch (id) {
case QWebPage::OpenLink:
openLinkInExternalBrowser(url);
break;
case QWebPage::OpenLinkInNewWindow:
openLinkInNewWindow(url);
break;
default:
break;
}
}
/* TRANSLATOR Gui::BrowserView */
/**
* Constructs a BrowserView which is a child of 'parent', with the
* name 'name'.
*/
BrowserView::BrowserView(QWidget* parent)
: MDIView(0,parent,0),
WindowParameter( "Browser" ),
isLoading(false),
textSizeMultiplier(1.0)
{
view = new WebView(this);
setCentralWidget(view);
view->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
view->page()->setForwardUnsupportedContent(true);
// set our custom cookie manager
FcCookieJar* cookiejar = new FcCookieJar(this);
view->page()->networkAccessManager()->setCookieJar(cookiejar);
// enable local storage so we can store stuff across sessions (startpage)
QWebSettings* settings = view->settings();
settings->setAttribute(QWebSettings::LocalStorageEnabled, true);
settings->setLocalStoragePath(QString::fromUtf8((App::Application::getUserAppDataDir()+"webdata").c_str()));
// setting background to white
QPalette palette = view->palette();
palette.setBrush(QPalette::Base, Qt::white);
view->page()->setPalette(palette);
view->setAttribute(Qt::WA_OpaquePaintEvent, true);
connect(view, SIGNAL(loadStarted()),
this, SLOT(onLoadStarted()));
connect(view, SIGNAL(loadProgress(int)),
this, SLOT(onLoadProgress(int)));
connect(view, SIGNAL(loadFinished(bool)),
this, SLOT(onLoadFinished(bool)));
connect(view, SIGNAL(linkClicked(const QUrl &)),
this, SLOT(onLinkClicked(const QUrl &)));
connect(view, SIGNAL(openLinkInExternalBrowser(const QUrl &)),
this, SLOT(onOpenLinkInExternalBrowser(const QUrl &)));
connect(view, SIGNAL(openLinkInNewWindow(const QUrl &)),
this, SLOT(onOpenLinkInNewWindow(const QUrl &)));
connect(view->page(), SIGNAL(downloadRequested(const QNetworkRequest &)),
this, SLOT(onDownloadRequested(const QNetworkRequest &)));
connect(view->page(), SIGNAL(unsupportedContent(QNetworkReply*)),
this, SLOT(onUnsupportedContent(QNetworkReply*)));
}
/** Destroys the object and frees any allocated resources */
BrowserView::~BrowserView()
{
delete view;
}
void BrowserView::onLinkClicked (const QUrl & url)
{
QString scheme = url.scheme();
QString host = url.host();
//QString username = url.userName();
// path handling
QString path = url.path();
QFileInfo fi(path);
QString ext = fi.completeSuffix();
QUrl exturl(url);
// query
QString q;
if (url.hasQuery())
#if QT_VERSION >= 0x050000
q = url.query();
#else
q = QString::fromAscii(url.encodedQuery().data());
#endif
//QString fragment = url. fragment();
if (scheme==QString::fromLatin1("http") || scheme==QString::fromLatin1("https")) {
load(url);
}
// Small trick to force opening a link in an external browser: use exthttp or exthttps
// Write your URL as exthttp://www.example.com
else if (scheme==QString::fromLatin1("exthttp")) {
exturl.setScheme(QString::fromLatin1("http"));
QDesktopServices::openUrl(exturl);
}
else if (scheme==QString::fromLatin1("exthttps")) {
exturl.setScheme(QString::fromLatin1("https"));
QDesktopServices::openUrl(exturl);
}
// run scripts if not from somewhere else!
if ((scheme.size() < 2 || scheme==QString::fromLatin1("file"))&& host.isEmpty()) {
QFileInfo fi(path);
if (fi.exists()) {
QString ext = fi.completeSuffix();
if (ext == QString::fromLatin1("py")) {
try {
if (!q.isEmpty()) {
// encapsulate the value in quotes
q = q.replace(QString::fromLatin1("="),QString::fromLatin1("=\""))+QString::fromLatin1("\"");
q = q.replace(QString::fromLatin1("%"),QString::fromLatin1("%%"));
// url queries in the form of somescript.py?key=value, the first key=value will be printed in the py console as key="value"
Gui::Command::doCommand(Gui::Command::Gui,q.toStdString().c_str());
}
// Gui::Command::doCommand(Gui::Command::Gui,"execfile('%s')",(const char*) fi.absoluteFilePath(). toLocal8Bit());
Gui::Command::doCommand(Gui::Command::Gui,"exec(open('%s').read())",(const char*) fi.absoluteFilePath() .toLocal8Bit());
}
catch (const Base::RestoreError& e) {
e.ReportException();
if(e.getTranslatable()) {
QMessageBox::critical(Gui::getMainWindow(), QObject::tr("Error loading file"),
QObject::tr(e.getMessage().c_str()));
}
}
catch (const Base::Exception& e) {
QMessageBox::critical(this, tr("Error"), QString::fromUtf8(e.what()));
}
}
}
else {
QMessageBox::warning(Gui::getMainWindow(), QObject::tr("File does not exist!"),
fi.absoluteFilePath ());
}
}
}
bool BrowserView::chckHostAllowed(const QString& host)
{
// only check if a local file, later we can do here a dialog to ask the user if
return host.isEmpty();
}
void BrowserView::onDownloadRequested(const QNetworkRequest & request)
{
Gui::Dialog::DownloadManager::getInstance()->download(request);
}
void BrowserView::onUnsupportedContent(QNetworkReply* reply)
{
// Do not call handleUnsupportedContent() directly otherwise we won't get
// the metaDataChanged() signal of the reply.
Gui::Dialog::DownloadManager::getInstance()->download(reply->url());
// Due to setting the policy QWebPage::DelegateAllLinks the onLinkClicked()
// slot is called even when clicking on a downloadable file but the page
// then fails to load. Thus, we reload the previous url.
view->reload();
}
void BrowserView::load(const char* URL)
{
QUrl url = QUrl::fromUserInput(QString::fromUtf8(URL));
load(url);
}
void BrowserView::load(const QUrl & url)
{
if (isLoading)
stop();
view->load(url);
view->setUrl(url);
if (url.scheme().size() < 2) {
QString path = url.path();
QFileInfo fi(path);
QString name = fi.baseName();
setWindowTitle(name);
}
else {
setWindowTitle(url.host());
}
setWindowIcon(QWebSettings::iconForUrl(url));
}
void BrowserView::setHtml(const QString& HtmlCode,const QUrl & BaseUrl)
{
if (isLoading)
stop();
view->setHtml(HtmlCode,BaseUrl);
setWindowIcon(QWebSettings::iconForUrl(BaseUrl));
}
void BrowserView::stop(void)
{
view->stop();
}
void BrowserView::onLoadStarted()
{
QProgressBar* bar = Gui::Sequencer::instance()->getProgressBar();
bar->setRange(0, 100);
bar->show();
Gui::getMainWindow()->showMessage(tr("Loading %1...").arg(view->url().toString()));
isLoading = true;
}
void BrowserView::onLoadProgress(int step)
{
QProgressBar* bar = Gui::Sequencer::instance()->getProgressBar();
bar->setValue(step);
}
void BrowserView::onLoadFinished(bool ok)
{
if (ok) {
QProgressBar* bar = Sequencer::instance()->getProgressBar();
bar->setValue(100);
bar->hide();
getMainWindow()->showMessage(QString());
}
isLoading = false;
}
void BrowserView::onOpenLinkInExternalBrowser(const QUrl& url)
{
QDesktopServices::openUrl(url);
}
void BrowserView::onOpenLinkInNewWindow(const QUrl& url)
{
BrowserView* view = new WebGui::BrowserView(Gui::getMainWindow());
view->setWindowTitle(QObject::tr("Browser"));
view->resize(400, 300);
view->load(url);
Gui::getMainWindow()->addWindow(view);
Gui::getMainWindow()->setActiveWindow(this);
}
void BrowserView::OnChange(Base::Subject<const char*> &rCaller,const char* rcReason)
{
Q_UNUSED(rCaller);
Q_UNUSED(rcReason);
}
/**
* Runs the action specified by \a pMsg.
*/
bool BrowserView::onMsg(const char* pMsg,const char** )
{
if (strcmp(pMsg,"Back")==0){
view->back();
return true;
} else if (strcmp(pMsg,"Next")==0){
view->forward();
return true;
} else if (strcmp(pMsg,"Refresh")==0){
view->reload();
return true;
} else if (strcmp(pMsg,"Stop")==0){
stop();
return true;
} else if (strcmp(pMsg,"ZoomIn")==0){
textSizeMultiplier += 0.2f;
view->setTextSizeMultiplier(textSizeMultiplier);
return true;
} else if (strcmp(pMsg,"ZoomOut")==0){
textSizeMultiplier -= 0.2f;
view->setTextSizeMultiplier(textSizeMultiplier);
return true;
}
return false;
}
/**
* Checks if the action \a pMsg is available. This is for enabling/disabling
* the corresponding buttons or menu items for this action.
*/
bool BrowserView::onHasMsg(const char* pMsg) const
{
if (strcmp(pMsg,"Back")==0) return true;
if (strcmp(pMsg,"Next")==0) return true;
if (strcmp(pMsg,"Refresh")==0) return !isLoading;
if (strcmp(pMsg,"Stop")==0) return isLoading;
if (strcmp(pMsg,"ZoomIn")==0) return true;
if (strcmp(pMsg,"ZoomOut")==0) return true;
return false;
}
/** Checking on close state. */
bool BrowserView::canClose(void)
{
return true;
}
PyObject* BrowserView::getPyObject(void)
{
static bool init = false;
if (!init) {
init = true;
BrowserViewPy::init_type();
}
return new BrowserViewPy(this);
}
#include "moc_BrowserView.cpp"