summaryrefslogtreecommitdiffstats
path: root/bibletime/frontend/display/chtmlreaddisplay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bibletime/frontend/display/chtmlreaddisplay.cpp')
-rw-r--r--bibletime/frontend/display/chtmlreaddisplay.cpp507
1 files changed, 507 insertions, 0 deletions
diff --git a/bibletime/frontend/display/chtmlreaddisplay.cpp b/bibletime/frontend/display/chtmlreaddisplay.cpp
new file mode 100644
index 0000000..4219f33
--- /dev/null
+++ b/bibletime/frontend/display/chtmlreaddisplay.cpp
@@ -0,0 +1,507 @@
+/*********
+*
+* This file is part of BibleTime's source code, http://www.bibletime.info/.
+*
+* Copyright 1999-2006 by the BibleTime developers.
+* The BibleTime source code is licensed under the GNU General Public License version 2.0.
+*
+**********/
+
+
+
+#include "chtmlreaddisplay.h"
+
+#include "frontend/displaywindow/cdisplaywindow.h"
+#include "frontend/displaywindow/creadwindow.h"
+#include "backend/creferencemanager.h"
+#include "backend/cswordkey.h"
+
+#include "frontend/cbtconfig.h"
+#include "frontend/cdragdropmgr.h"
+#include "frontend/cinfodisplay.h"
+
+#include "util/ctoolclass.h"
+#include "util/cpointers.h"
+#include "util/scoped_resource.h"
+
+//We will need to reference this in the Qt includes
+#include <kdeversion.h>
+
+//Qt includes
+#include <qcursor.h>
+#include <qscrollview.h>
+#include <qwidget.h>
+#include <qdragobject.h>
+#include <qpopupmenu.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#if KDE_VERSION < 0x030300
+//We will need to show the error message.
+#include <qmessagebox.h>
+#endif
+
+//KDE includes
+#include <kapplication.h>
+#include <khtmlview.h>
+#include <kglobalsettings.h>
+#include <khtml_events.h>
+
+#include <dom/dom2_range.h>
+#include <dom/html_element.h>
+#include <dom/dom2_traversal.h>
+
+using namespace InfoDisplay;
+
+CHTMLReadDisplay::CHTMLReadDisplay(CReadWindow* readWindow, QWidget* parentWidget)
+: KHTMLPart((m_view = new CHTMLReadDisplayView(this, parentWidget ? parentWidget : readWindow)), readWindow ? readWindow : parentWidget),
+CReadDisplay(readWindow),
+m_currentAnchorCache(QString::null) {
+ setDNDEnabled(false);
+ setJavaEnabled(false);
+ setJScriptEnabled(false);
+ setPluginsEnabled(false);
+
+ m_view->setDragAutoScroll(false);
+
+}
+
+CHTMLReadDisplay::~CHTMLReadDisplay() {}
+
+const QString CHTMLReadDisplay::text( const CDisplay::TextType format, const CDisplay::TextPart part) {
+
+ switch (part) {
+ case Document: {
+ if (format == HTMLText) {
+ return document().toHTML();
+ }
+ else {
+ CDisplayWindow* window = parentWindow();
+ CSwordKey* const key = window->key();
+ CSwordModuleInfo* module = key->module();
+ //return htmlDocument().body().innerText().string().latin1();
+ //This function is never used for Bibles, so it is not
+ //implemented. If it should be, see CReadDisplay::print() for
+ //example code.
+ Q_ASSERT(module->type() == CSwordModuleInfo::Lexicon || module->type() == CSwordModuleInfo::Commentary || module->type() == CSwordModuleInfo::GenericBook);
+ if (module->type() == CSwordModuleInfo::Lexicon || module->type() == CSwordModuleInfo::Commentary || module->type() == CSwordModuleInfo::GenericBook) {
+ //TODO: This is a BAD HACK, we have to fnd a better solution to manage the settings now
+ CSwordBackend::FilterOptions filterOptions;
+ filterOptions.footnotes = false;
+ filterOptions.strongNumbers = false;
+ filterOptions.morphTags = false;
+ filterOptions.lemmas = false;
+ filterOptions.scriptureReferences = false;
+ filterOptions.textualVariants = false;
+
+ CPointers::backend()->setFilterOptions(filterOptions);
+
+ return QString(key->strippedText()).append("\n(")
+ .append(key->key())
+ .append(", ")
+ .append(key->module()->name())
+ .append(")");
+ }
+ }
+ }
+
+ case SelectedText: {
+ if (!hasSelection()) {
+ return QString::null;
+ }
+ else if (format == HTMLText) {
+ DOM::Range range = selection();
+ return range.toHTML().string();
+ }
+ else { //plain text requested
+ return selectedText();
+ }
+ }
+
+ case AnchorOnly: {
+ QString moduleName;
+ QString keyName;
+ CReferenceManager::Type type;
+ CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type);
+
+ return keyName;
+ }
+
+ case AnchorTextOnly: {
+ QString moduleName;
+ QString keyName;
+ CReferenceManager::Type type;
+ CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type);
+
+ if (CSwordModuleInfo* module = backend()->findModuleByName(moduleName)) {
+ util::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(module) );
+ key->key( keyName );
+
+ return key->strippedText();
+ }
+ return QString::null;
+ }
+
+ case AnchorWithText: {
+ QString moduleName;
+ QString keyName;
+ CReferenceManager::Type type;
+ CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type);
+
+ if (CSwordModuleInfo* module = backend()->findModuleByName(moduleName)) {
+ util::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(module) );
+ key->key( keyName );
+
+ //TODO: This is a BAD HACK, we have to fnd a better solution to manage the settings now
+ CSwordBackend::FilterOptions filterOptions;
+ filterOptions.footnotes = false;
+ filterOptions.strongNumbers = false;
+ filterOptions.morphTags = false;
+ filterOptions.lemmas = false;
+ filterOptions.scriptureReferences = false;
+ filterOptions.textualVariants = false;
+
+ CPointers::backend()->setFilterOptions(filterOptions);
+
+ return QString(key->strippedText()).append("\n(")
+ .append(key->key())
+ .append(", ")
+ .append(key->module()->name())
+ .append(")");
+ /* ("%1\n(%2, %3)")
+ .arg()
+ .arg(key->key())
+ .arg(key->module()->name());*/
+ }
+ return QString::null;
+ }
+ default:
+ return QString::null;
+ }
+}
+
+void CHTMLReadDisplay::setText( const QString& newText ) {
+ begin();
+ write(newText);
+ end();
+}
+
+/** No descriptions */
+const bool CHTMLReadDisplay::hasSelection() {
+ return KHTMLPart::hasSelection();
+}
+
+
+/** Reimplementation. */
+QScrollView* CHTMLReadDisplay::view() {
+ return KHTMLPart::view();
+}
+
+void CHTMLReadDisplay::selectAll() {
+ KHTMLPart::selectAll();
+}
+
+/** No descriptions */
+void CHTMLReadDisplay::moveToAnchor( const QString& anchor ) {
+ m_currentAnchorCache = anchor;
+
+ //This is an ugly hack to work around a KDE problem in KDE including 3.3.1 (no later versions tested so far)
+ QTimer::singleShot(0, this, SLOT(slotGoToAnchor()));
+
+ // instead of:
+ // slotGoToAnchor();
+}
+
+void CHTMLReadDisplay::urlSelected( const QString& url, int button, int state, const QString& _target, KParts::URLArgs args) {
+ KHTMLPart::urlSelected(url, button, state, _target, args);
+ m_urlWorkaroundData.doWorkaround = false;
+ // qWarning("clicked: %s", url.latin1());
+ if (!url.isEmpty() && CReferenceManager::isHyperlink(url)) {
+ QString module;
+ QString key;
+ CReferenceManager::Type type;
+
+ CReferenceManager::decodeHyperlink(url, module, key, type);
+ if (module.isEmpty()) {
+ module = CReferenceManager::preferredModule( type );
+ }
+
+ // we have to use this workaround, otherwise the widget would scroll because it was interrupted
+ // between mouseClick and mouseRelease (I guess)
+ m_urlWorkaroundData.doWorkaround = true;
+ m_urlWorkaroundData.url = url;
+ m_urlWorkaroundData.state = state;
+ m_urlWorkaroundData.button = button;
+ m_urlWorkaroundData.target = _target;
+ m_urlWorkaroundData.args = args;
+ m_urlWorkaroundData.module = module;
+ m_urlWorkaroundData.key = key;
+ }
+ else if (!url.isEmpty() && (url.left(1) == "#")) { //anchor
+ moveToAnchor(url.mid(1));
+ }
+ else if (url.left(7) == "http://") { //open the bowser configured by kdeb
+ KApplication::kApplication()->invokeBrowser( url ); //ToDo: Not yet tested
+ }
+}
+
+/** Reimplementation. */
+void CHTMLReadDisplay::khtmlMouseReleaseEvent( khtml::MouseReleaseEvent* event ) {
+ KHTMLPart::khtmlMouseReleaseEvent(event);
+
+ m_dndData.mousePressed = false;
+ m_dndData.isDragging = false;
+ m_dndData.node = DOM::Node();
+ m_dndData.anchor = DOM::DOMString();
+
+ if (m_urlWorkaroundData.doWorkaround) {
+ m_urlWorkaroundData.doWorkaround = false;
+ connectionsProxy()->emitReferenceClicked(
+ m_urlWorkaroundData.module,
+ m_urlWorkaroundData.key
+ );
+ }
+}
+
+void CHTMLReadDisplay::khtmlMousePressEvent( khtml::MousePressEvent* event ) {
+ m_dndData.node = DOM::Node();
+ m_dndData.anchor = DOM::DOMString();
+ m_dndData.mousePressed = false;
+ m_dndData.isDragging = false;
+
+ if (event->qmouseEvent()->button() == Qt::RightButton) {
+ DOM::Node tmpNode = event->innerNode();
+ DOM::Node attr;
+ m_nodeInfo[CDisplay::Lemma] = QString::null;
+
+ do {
+ if (!tmpNode.isNull() && (tmpNode.nodeType() ==
+ DOM::Node::ELEMENT_NODE) && tmpNode.hasAttributes()) {
+ attr = tmpNode.attributes().getNamedItem("lemma");
+ if (!attr.isNull()) {
+ m_nodeInfo[ CDisplay::Lemma ] = attr.nodeValue().string();
+ break;
+ }
+ }
+ tmpNode = tmpNode.parentNode();
+ } while ( !tmpNode.isNull() );
+
+ setActiveAnchor( event->url().string() );
+ }
+ else if (event->qmouseEvent()->button() == Qt::LeftButton) {
+ m_dndData.node = event->innerNode();
+ m_dndData.anchor = event->url();
+ m_dndData.mousePressed = true;
+ m_dndData.isDragging = false;
+ m_dndData.startPos = QPoint(event->x(), event->y());
+ m_dndData.selection = selectedText();
+
+ if (!m_dndData.node.isNull()) { //we drag a valid link
+ m_dndData.dragType = DNDData::Link;
+ }
+ }
+
+ KHTMLPart::khtmlMousePressEvent(event);
+}
+
+/** Reimplementation for our drag&drop system. Also needed for the mouse tracking */
+void CHTMLReadDisplay::khtmlMouseMoveEvent( khtml::MouseMoveEvent* e ) {
+ if( e->qmouseEvent()->state() & LeftButton == LeftButton) { //left mouse button pressed
+ const int delay = KGlobalSettings::dndEventDelay();
+ QPoint newPos = QPoint(e->x(), e->y());
+
+ if ( (newPos.x() > m_dndData.startPos.x()+delay || newPos.x() < (m_dndData.startPos.x()-delay) ||
+ newPos.y() > m_dndData.startPos.y()+delay || newPos.y() < (m_dndData.startPos.y()-delay)) &&
+ !m_dndData.isDragging && m_dndData.mousePressed ) {
+ QDragObject* d = 0;
+ if (!m_dndData.anchor.isEmpty() && (m_dndData.dragType == DNDData::Link) && !m_dndData.node.isNull() ) {
+ // create a new bookmark drag!
+ QString module = QString::null;
+ QString key = QString::null;
+ CReferenceManager::Type type;
+ if ( !CReferenceManager::decodeHyperlink(m_dndData.anchor.string(), module, key, type) )
+ return;
+
+ CDragDropMgr::ItemList dndItems;
+ //no description!
+ dndItems.append( CDragDropMgr::Item(module, key, QString::null) );
+ d = CDragDropMgr::dragObject(dndItems, KHTMLPart::view()->viewport());
+ }
+ else if ((m_dndData.dragType == DNDData::Text) && !m_dndData.selection.isEmpty()) {
+ // create a new plain text drag!
+ CDragDropMgr::ItemList dndItems;
+ dndItems.append( CDragDropMgr::Item(m_dndData.selection) ); //no description!
+ d = CDragDropMgr::dragObject(dndItems, KHTMLPart::view()->viewport());
+ }
+
+ if (d) {
+ m_dndData.isDragging = true;
+ m_dndData.mousePressed = false;
+
+ //first make a virtual mouse click to end the selection, it it's in progress
+ QMouseEvent e(QEvent::MouseButtonRelease, QPoint(0,0), Qt::LeftButton, Qt::LeftButton);
+ KApplication::sendEvent(view()->viewport(), &e);
+ d->drag();
+ }
+ }
+ }
+ else if (getMouseTracking() && !(e->qmouseEvent()->state() & Qt::ShiftButton == Qt::ShiftButton)) {
+ //no mouse button pressed and tracking enabled
+ DOM::Node node = e->innerNode();
+ //if no link was under the mouse try to find a title attribute
+ if (!node.isNull() && (m_previousEventNode != node)) {
+ // we want to avoid processing the node again
+ // After some millisecs the new timer activates the Mag window update, see timerEvent()
+ // SHIFT key not pressed, so we start timer
+ if ( !(e->qmouseEvent()->state() & Qt::ShiftButton)) {
+ // QObject has simple timer
+ killTimers();
+ startTimer( CBTConfig::get(CBTConfig::magDelay) );
+ }
+
+ m_previousEventNode = node;
+ }
+ }
+
+ KHTMLPart::khtmlMouseMoveEvent(e);
+}
+
+/** The Mag window update happens here if the mouse has not moved to another node after starting the timer.*/
+void CHTMLReadDisplay::timerEvent( QTimerEvent *e ) {
+ killTimers();
+ DOM::Node currentNode = nodeUnderMouse();
+ CInfoDisplay::ListInfoData infoList;
+
+ // Process the node under cursor if it is the same as at the start of the timer
+ if (!currentNode.isNull() && (currentNode != m_previousEventNode) && this->view()->hasMouse()) {
+ DOM::Node attr;
+ do {
+ if (!currentNode.isNull() && (currentNode.nodeType() == DOM::Node::ELEMENT_NODE) && currentNode.hasAttributes()) { //found right node
+ attr = currentNode.attributes().getNamedItem("note");
+ if (!attr.isNull()) {
+ infoList.append( qMakePair(CInfoDisplay::Footnote, attr.nodeValue().string()) );
+ }
+
+ attr = currentNode.attributes().getNamedItem("lemma");
+ if (!attr.isNull()) {
+ infoList.append( qMakePair(CInfoDisplay::Lemma, attr.nodeValue().string()) );
+ }
+
+ attr = currentNode.attributes().getNamedItem("morph");
+ if (!attr.isNull()) {
+ infoList.append( qMakePair(CInfoDisplay::Morph, attr.nodeValue().string()) );
+ }
+
+ attr = currentNode.attributes().getNamedItem("expansion");
+ if (!attr.isNull()) {
+ infoList.append( qMakePair(CInfoDisplay::Abbreviation, attr.nodeValue().string()) );
+ }
+
+ attr = currentNode.attributes().getNamedItem("crossrefs");
+ if (!attr.isNull()) {
+ infoList.append( qMakePair(CInfoDisplay::CrossReference, attr.nodeValue().string()) );
+ }
+ }
+
+ currentNode = currentNode.parentNode();
+ if (!currentNode.isNull() && currentNode.hasAttributes()) {
+ attr = currentNode.attributes().getNamedItem("class");
+ if (!attr.isNull() && (attr.nodeValue().string() == "entry") || (attr.nodeValue().string() == "currententry") ) {
+ break;
+ }
+ }
+ }
+ while ( !currentNode.isNull() );
+ }
+
+ // Update the mag if there is new content
+ if (!(infoList.isEmpty())) {
+ CPointers::infoDisplay()->setInfo(infoList);
+ }
+
+}
+
+// ---------------------
+
+CHTMLReadDisplayView::CHTMLReadDisplayView(CHTMLReadDisplay* displayWidget, QWidget* parent) : KHTMLView(displayWidget, parent), m_display(displayWidget) {
+ viewport()->setAcceptDrops(true);
+ setMarginWidth(4);
+ setMarginHeight(4);
+};
+
+
+/** Opens the popupmenu at the given position. */
+void CHTMLReadDisplayView::popupMenu( const QString& url, const QPoint& pos) {
+ if (!url.isEmpty()) {
+ m_display->setActiveAnchor(url);
+ }
+ if (QPopupMenu* popup = m_display->installedPopup()) {
+ popup->exec(pos);
+ }
+}
+
+/** Reimplementation from QScrollView. Sets the right slots */
+void CHTMLReadDisplayView::polish() {
+ KHTMLView::polish();
+ connect( part(), SIGNAL(popupMenu(const QString&, const QPoint&)),
+ this, SLOT(popupMenu(const QString&, const QPoint&)));
+}
+
+/** Reimplementatiob from QScrollView. */
+void CHTMLReadDisplayView::contentsDropEvent( QDropEvent* e ) {
+ if (CDragDropMgr::canDecode(e) && CDragDropMgr::dndType(e) == CDragDropMgr::Item::Bookmark) {
+ CDragDropMgr::ItemList dndItems = CDragDropMgr::decode(e);
+ CDragDropMgr::Item item = dndItems.first();
+ e->acceptAction();
+
+ m_display->connectionsProxy()->emitReferenceDropped(item.bookmarkKey());
+ return;
+ };
+
+ //don't accept the action!
+ e->acceptAction(false);
+ e->ignore();
+}
+
+/** Reimplementation from QScrollView. */
+void CHTMLReadDisplayView::contentsDragEnterEvent( QDragEnterEvent* e ) {
+ if (CDragDropMgr::canDecode(e) && CDragDropMgr::dndType(e) == CDragDropMgr::Item::Bookmark) {
+ e->acceptAction();
+ return;
+ }
+
+ e->acceptAction(false);
+ e->ignore();
+}
+
+/*!
+\fn CHTMLReadDisplay::slotPageLoaded()
+*/
+void CHTMLReadDisplay::slotGoToAnchor() {
+ if (!m_currentAnchorCache.isEmpty()) {
+ if (!gotoAnchor( m_currentAnchorCache ) ) {
+ qDebug("anchor %s not present!", m_currentAnchorCache.latin1());
+ }
+ }
+ m_currentAnchorCache = QString::null;
+}
+
+void CHTMLReadDisplay::zoomIn() {
+ setZoomFactor( (int)((float)zoomFactor()*1.1) );
+}
+
+void CHTMLReadDisplay::zoomOut() {
+ setZoomFactor( (int)((float)zoomFactor()*(1.0/1.1)) );
+}
+
+void CHTMLReadDisplay::openFindTextDialog() {
+#if KDE_VERSION >= 0x030300
+ findText();
+#else
+ QMessageBox::information(0, "Not Supported",
+ "This copy of BibleTime was built against a version of KDE older\n"
+ "than 3.3 (probably due to your distro), so this search feature\n"
+ "does not work.\n\n"
+ "This is a known issue. If we do not have a fix for the next\n"
+ "version of BibleTime, we will remove the option.");
+#endif
+}