/********* * * 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 TQt includes #include //TQt includes #include #include #include #include #include #include #include #if TDE_VERSION < 0x030300 //We will need to show the error message. #include #endif //KDE includes #include #include #include #include #include #include #include using namespace InfoDisplay; CHTMLReadDisplay::CHTMLReadDisplay(CReadWindow* readWindow, TQWidget* parentWidget) : TDEHTMLPart((m_view = new CHTMLReadDisplayView(this, parentWidget ? parentWidget : readWindow)), TQT_TQOBJECT(readWindow ? readWindow : parentWidget)), CReadDisplay(readWindow), m_currentAnchorCache(TQString()) { setDNDEnabled(false); setJavaEnabled(false); setJScriptEnabled(false); setPluginsEnabled(false); m_view->setDragAutoScroll(false); } CHTMLReadDisplay::~CHTMLReadDisplay() {} const TQString 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 TQString(key->strippedText()).append("\n(") .append(key->key()) .append(", ") .append(key->module()->name()) .append(")"); } } } case SelectedText: { if (!hasSelection()) { return TQString(); } else if (format == HTMLText) { DOM::Range range = selection(); return range.toHTML().string(); } else { //plain text requested return selectedText(); } } case AnchorOnly: { TQString moduleName; TQString keyName; CReferenceManager::Type type; CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type); return keyName; } case AnchorTextOnly: { TQString moduleName; TQString keyName; CReferenceManager::Type type; CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type); if (CSwordModuleInfo* module = backend()->findModuleByName(moduleName)) { util::scoped_ptr key( CSwordKey::createInstance(module) ); key->key( keyName ); return key->strippedText(); } return TQString(); } case AnchorWithText: { TQString moduleName; TQString keyName; CReferenceManager::Type type; CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type); if (CSwordModuleInfo* module = backend()->findModuleByName(moduleName)) { util::scoped_ptr 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 TQString(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 TQString(); } default: return TQString(); } } void CHTMLReadDisplay::setText( const TQString& newText ) { begin(); write(newText); end(); } /** No descriptions */ const bool CHTMLReadDisplay::hasSelection() { return TDEHTMLPart::hasSelection(); } /** Reimplementation. */ TQScrollView* CHTMLReadDisplay::view() { return TDEHTMLPart::view(); } void CHTMLReadDisplay::selectAll() { TDEHTMLPart::selectAll(); } /** No descriptions */ void CHTMLReadDisplay::moveToAnchor( const TQString& 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) TQTimer::singleShot(0, this, TQT_SLOT(slotGoToAnchor())); // instead of: // slotGoToAnchor(); } void CHTMLReadDisplay::urlSelected( const TQString& url, int button, int state, const TQString& _target, KParts::URLArgs args) { TDEHTMLPart::urlSelected(url, button, state, _target, args); m_urlWorkaroundData.doWorkaround = false; // tqWarning("clicked: %s", url.latin1()); if (!url.isEmpty() && CReferenceManager::isHyperlink(url)) { TQString module; TQString 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 TDEApplication::kApplication()->invokeBrowser( url ); //ToDo: Not yet tested } } /** Reimplementation. */ void CHTMLReadDisplay::tdehtmlMouseReleaseEvent( tdehtml::MouseReleaseEvent* event ) { TDEHTMLPart::tdehtmlMouseReleaseEvent(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::tdehtmlMousePressEvent( tdehtml::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] = TQString(); 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 = TQPoint(event->x(), event->y()); m_dndData.selection = selectedText(); if (!m_dndData.node.isNull()) { //we drag a valid link m_dndData.dragType = DNDData::Link; } } TDEHTMLPart::tdehtmlMousePressEvent(event); } /** Reimplementation for our drag&drop system. Also needed for the mouse tracking */ void CHTMLReadDisplay::tdehtmlMouseMoveEvent( tdehtml::MouseMoveEvent* e ) { if( e->qmouseEvent()->state() & Qt::LeftButton == Qt::LeftButton) { //left mouse button pressed const int delay = TDEGlobalSettings::dndEventDelay(); TQPoint newPos = TQPoint(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 ) { TQDragObject* d = 0; if (!m_dndData.anchor.isEmpty() && (m_dndData.dragType == DNDData::Link) && !m_dndData.node.isNull() ) { // create a new bookmark drag! TQString module = TQString(); TQString key = TQString(); 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, TQString()) ); d = CDragDropMgr::dragObject(dndItems, TDEHTMLPart::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, TDEHTMLPart::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 TQMouseEvent e(TQEvent::MouseButtonRelease, TQPoint(0,0), Qt::LeftButton, Qt::LeftButton); TDEApplication::sendEvent(view()->viewport(), &e); d->drag(); } } } else if (getMouseTracking() && !(e->qmouseEvent()->state() & TQt::ShiftButton == TQt::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() & TQt::ShiftButton)) { // TQObject has simple timer killTimers(); startTimer( CBTConfig::get(CBTConfig::magDelay) ); } m_previousEventNode = node; } } TDEHTMLPart::tdehtmlMouseMoveEvent(e); } /** The Mag window update happens here if the mouse has not moved to another node after starting the timer.*/ void CHTMLReadDisplay::timerEvent( TQTimerEvent *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( tqMakePair(CInfoDisplay::Footnote, attr.nodeValue().string()) ); } attr = currentNode.attributes().getNamedItem("lemma"); if (!attr.isNull()) { infoList.append( tqMakePair(CInfoDisplay::Lemma, attr.nodeValue().string()) ); } attr = currentNode.attributes().getNamedItem("morph"); if (!attr.isNull()) { infoList.append( tqMakePair(CInfoDisplay::Morph, attr.nodeValue().string()) ); } attr = currentNode.attributes().getNamedItem("expansion"); if (!attr.isNull()) { infoList.append( tqMakePair(CInfoDisplay::Abbreviation, attr.nodeValue().string()) ); } attr = currentNode.attributes().getNamedItem("crossrefs"); if (!attr.isNull()) { infoList.append( tqMakePair(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, TQWidget* parent) : TDEHTMLView(displayWidget, parent), m_display(displayWidget) { viewport()->setAcceptDrops(true); setMarginWidth(4); setMarginHeight(4); }; /** Opens the popupmenu at the given position. */ void CHTMLReadDisplayView::popupMenu( const TQString& url, const TQPoint& pos) { if (!url.isEmpty()) { m_display->setActiveAnchor(url); } if (TQPopupMenu* popup = m_display->installedPopup()) { popup->exec(pos); } } /** Reimplementation from TQScrollView. Sets the right slots */ void CHTMLReadDisplayView::polish() { TDEHTMLView::polish(); connect( part(), TQT_SIGNAL(popupMenu(const TQString&, const TQPoint&)), this, TQT_SLOT(popupMenu(const TQString&, const TQPoint&))); } /** Reimplementatiob from TQScrollView. */ void CHTMLReadDisplayView::contentsDropEvent( TQDropEvent* 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 TQScrollView. */ void CHTMLReadDisplayView::contentsDragEnterEvent( TQDragEnterEvent* 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 ) ) { tqDebug("anchor %s not present!", m_currentAnchorCache.latin1()); } } m_currentAnchorCache = TQString(); } void CHTMLReadDisplay::zoomIn() { setZoomFactor( (int)((float)zoomFactor()*1.1) ); } void CHTMLReadDisplay::zoomOut() { setZoomFactor( (int)((float)zoomFactor()*(1.0/1.1)) ); } void CHTMLReadDisplay::openFindTextDialog() { #if TDE_VERSION >= 0x030300 findText(); #else TQMessageBox::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 }