Bibletime – a bible study tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

chtmlreaddisplay.cpp 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. /*********
  2. *
  3. * This file is part of BibleTime's source code, http://www.bibletime.info/.
  4. *
  5. * Copyright 1999-2006 by the BibleTime developers.
  6. * The BibleTime source code is licensed under the GNU General Public License version 2.0.
  7. *
  8. **********/
  9. #include "chtmlreaddisplay.h"
  10. #include "frontend/displaywindow/cdisplaywindow.h"
  11. #include "frontend/displaywindow/creadwindow.h"
  12. #include "backend/creferencemanager.h"
  13. #include "backend/cswordkey.h"
  14. #include "frontend/cbtconfig.h"
  15. #include "frontend/cdragdropmgr.h"
  16. #include "frontend/cinfodisplay.h"
  17. #include "util/ctoolclass.h"
  18. #include "util/cpointers.h"
  19. #include "util/scoped_resource.h"
  20. //We will need to reference this in the TQt includes
  21. #include <tdeversion.h>
  22. //TQt includes
  23. #include <tqcursor.h>
  24. #include <tqscrollview.h>
  25. #include <tqwidget.h>
  26. #include <tqdragobject.h>
  27. #include <tqpopupmenu.h>
  28. #include <tqlayout.h>
  29. #include <tqtimer.h>
  30. #if TDE_VERSION < 0x030300
  31. //We will need to show the error message.
  32. #include <tqmessagebox.h>
  33. #endif
  34. //KDE includes
  35. #include <tdeapplication.h>
  36. #include <tdehtmlview.h>
  37. #include <tdeglobalsettings.h>
  38. #include <tdehtml_events.h>
  39. #include <dom/dom2_range.h>
  40. #include <dom/html_element.h>
  41. #include <dom/dom2_traversal.h>
  42. using namespace InfoDisplay;
  43. CHTMLReadDisplay::CHTMLReadDisplay(CReadWindow* readWindow, TQWidget* parentWidget)
  44. : TDEHTMLPart((m_view = new CHTMLReadDisplayView(this, parentWidget ? parentWidget : readWindow)), TQT_TQOBJECT(readWindow ? readWindow : parentWidget)),
  45. CReadDisplay(readWindow),
  46. m_currentAnchorCache(TQString()) {
  47. setDNDEnabled(false);
  48. setJavaEnabled(false);
  49. setJScriptEnabled(false);
  50. setPluginsEnabled(false);
  51. m_view->setDragAutoScroll(false);
  52. }
  53. CHTMLReadDisplay::~CHTMLReadDisplay() {}
  54. const TQString CHTMLReadDisplay::text( const CDisplay::TextType format, const CDisplay::TextPart part) {
  55. switch (part) {
  56. case Document: {
  57. if (format == HTMLText) {
  58. return document().toHTML();
  59. }
  60. else {
  61. CDisplayWindow* window = parentWindow();
  62. CSwordKey* const key = window->key();
  63. CSwordModuleInfo* module = key->module();
  64. //return htmlDocument().body().innerText().string().latin1();
  65. //This function is never used for Bibles, so it is not
  66. //implemented. If it should be, see CReadDisplay::print() for
  67. //example code.
  68. Q_ASSERT(module->type() == CSwordModuleInfo::Lexicon || module->type() == CSwordModuleInfo::Commentary || module->type() == CSwordModuleInfo::GenericBook);
  69. if (module->type() == CSwordModuleInfo::Lexicon || module->type() == CSwordModuleInfo::Commentary || module->type() == CSwordModuleInfo::GenericBook) {
  70. //TODO: This is a BAD HACK, we have to fnd a better solution to manage the settings now
  71. CSwordBackend::FilterOptions filterOptions;
  72. filterOptions.footnotes = false;
  73. filterOptions.strongNumbers = false;
  74. filterOptions.morphTags = false;
  75. filterOptions.lemmas = false;
  76. filterOptions.scriptureReferences = false;
  77. filterOptions.textualVariants = false;
  78. CPointers::backend()->setFilterOptions(filterOptions);
  79. return TQString(key->strippedText()).append("\n(")
  80. .append(key->key())
  81. .append(", ")
  82. .append(key->module()->name())
  83. .append(")");
  84. }
  85. }
  86. }
  87. case SelectedText: {
  88. if (!hasSelection()) {
  89. return TQString();
  90. }
  91. else if (format == HTMLText) {
  92. DOM::Range range = selection();
  93. return range.toHTML().string();
  94. }
  95. else { //plain text requested
  96. return selectedText();
  97. }
  98. }
  99. case AnchorOnly: {
  100. TQString moduleName;
  101. TQString keyName;
  102. CReferenceManager::Type type;
  103. CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type);
  104. return keyName;
  105. }
  106. case AnchorTextOnly: {
  107. TQString moduleName;
  108. TQString keyName;
  109. CReferenceManager::Type type;
  110. CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type);
  111. if (CSwordModuleInfo* module = backend()->findModuleByName(moduleName)) {
  112. util::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(module) );
  113. key->key( keyName );
  114. return key->strippedText();
  115. }
  116. return TQString();
  117. }
  118. case AnchorWithText: {
  119. TQString moduleName;
  120. TQString keyName;
  121. CReferenceManager::Type type;
  122. CReferenceManager::decodeHyperlink(activeAnchor(), moduleName, keyName, type);
  123. if (CSwordModuleInfo* module = backend()->findModuleByName(moduleName)) {
  124. util::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(module) );
  125. key->key( keyName );
  126. //TODO: This is a BAD HACK, we have to fnd a better solution to manage the settings now
  127. CSwordBackend::FilterOptions filterOptions;
  128. filterOptions.footnotes = false;
  129. filterOptions.strongNumbers = false;
  130. filterOptions.morphTags = false;
  131. filterOptions.lemmas = false;
  132. filterOptions.scriptureReferences = false;
  133. filterOptions.textualVariants = false;
  134. CPointers::backend()->setFilterOptions(filterOptions);
  135. return TQString(key->strippedText()).append("\n(")
  136. .append(key->key())
  137. .append(", ")
  138. .append(key->module()->name())
  139. .append(")");
  140. /* ("%1\n(%2, %3)")
  141. .arg()
  142. .arg(key->key())
  143. .arg(key->module()->name());*/
  144. }
  145. return TQString();
  146. }
  147. default:
  148. return TQString();
  149. }
  150. }
  151. void CHTMLReadDisplay::setText( const TQString& newText ) {
  152. begin();
  153. write(newText);
  154. end();
  155. }
  156. /** No descriptions */
  157. const bool CHTMLReadDisplay::hasSelection() {
  158. return TDEHTMLPart::hasSelection();
  159. }
  160. /** Reimplementation. */
  161. TQScrollView* CHTMLReadDisplay::view() {
  162. return TDEHTMLPart::view();
  163. }
  164. void CHTMLReadDisplay::selectAll() {
  165. TDEHTMLPart::selectAll();
  166. }
  167. /** No descriptions */
  168. void CHTMLReadDisplay::moveToAnchor( const TQString& anchor ) {
  169. m_currentAnchorCache = anchor;
  170. //This is an ugly hack to work around a KDE problem in KDE including 3.3.1 (no later versions tested so far)
  171. TQTimer::singleShot(0, this, TQT_SLOT(slotGoToAnchor()));
  172. // instead of:
  173. // slotGoToAnchor();
  174. }
  175. void CHTMLReadDisplay::urlSelected( const TQString& url, int button, int state, const TQString& _target, KParts::URLArgs args) {
  176. TDEHTMLPart::urlSelected(url, button, state, _target, args);
  177. m_urlWorkaroundData.doWorkaround = false;
  178. // tqWarning("clicked: %s", url.latin1());
  179. if (!url.isEmpty() && CReferenceManager::isHyperlink(url)) {
  180. TQString module;
  181. TQString key;
  182. CReferenceManager::Type type;
  183. CReferenceManager::decodeHyperlink(url, module, key, type);
  184. if (module.isEmpty()) {
  185. module = CReferenceManager::preferredModule( type );
  186. }
  187. // we have to use this workaround, otherwise the widget would scroll because it was interrupted
  188. // between mouseClick and mouseRelease (I guess)
  189. m_urlWorkaroundData.doWorkaround = true;
  190. m_urlWorkaroundData.url = url;
  191. m_urlWorkaroundData.state = state;
  192. m_urlWorkaroundData.button = button;
  193. m_urlWorkaroundData.target = _target;
  194. m_urlWorkaroundData.args = args;
  195. m_urlWorkaroundData.module = module;
  196. m_urlWorkaroundData.key = key;
  197. }
  198. else if (!url.isEmpty() && (url.left(1) == "#")) { //anchor
  199. moveToAnchor(url.mid(1));
  200. }
  201. else if (url.left(7) == "http://") { //open the bowser configured by kdeb
  202. TDEApplication::kApplication()->invokeBrowser( url ); //ToDo: Not yet tested
  203. }
  204. }
  205. /** Reimplementation. */
  206. void CHTMLReadDisplay::tdehtmlMouseReleaseEvent( tdehtml::MouseReleaseEvent* event ) {
  207. TDEHTMLPart::tdehtmlMouseReleaseEvent(event);
  208. m_dndData.mousePressed = false;
  209. m_dndData.isDragging = false;
  210. m_dndData.node = DOM::Node();
  211. m_dndData.anchor = DOM::DOMString();
  212. if (m_urlWorkaroundData.doWorkaround) {
  213. m_urlWorkaroundData.doWorkaround = false;
  214. connectionsProxy()->emitReferenceClicked(
  215. m_urlWorkaroundData.module,
  216. m_urlWorkaroundData.key
  217. );
  218. }
  219. }
  220. void CHTMLReadDisplay::tdehtmlMousePressEvent( tdehtml::MousePressEvent* event ) {
  221. m_dndData.node = DOM::Node();
  222. m_dndData.anchor = DOM::DOMString();
  223. m_dndData.mousePressed = false;
  224. m_dndData.isDragging = false;
  225. if (event->qmouseEvent()->button() == Qt::RightButton) {
  226. DOM::Node tmpNode = event->innerNode();
  227. DOM::Node attr;
  228. m_nodeInfo[CDisplay::Lemma] = TQString();
  229. do {
  230. if (!tmpNode.isNull() && (tmpNode.nodeType() ==
  231. DOM::Node::ELEMENT_NODE) && tmpNode.hasAttributes()) {
  232. attr = tmpNode.attributes().getNamedItem("lemma");
  233. if (!attr.isNull()) {
  234. m_nodeInfo[ CDisplay::Lemma ] = attr.nodeValue().string();
  235. break;
  236. }
  237. }
  238. tmpNode = tmpNode.parentNode();
  239. } while ( !tmpNode.isNull() );
  240. setActiveAnchor( event->url().string() );
  241. }
  242. else if (event->qmouseEvent()->button() == Qt::LeftButton) {
  243. m_dndData.node = event->innerNode();
  244. m_dndData.anchor = event->url();
  245. m_dndData.mousePressed = true;
  246. m_dndData.isDragging = false;
  247. m_dndData.startPos = TQPoint(event->x(), event->y());
  248. m_dndData.selection = selectedText();
  249. if (!m_dndData.node.isNull()) { //we drag a valid link
  250. m_dndData.dragType = DNDData::Link;
  251. }
  252. }
  253. TDEHTMLPart::tdehtmlMousePressEvent(event);
  254. }
  255. /** Reimplementation for our drag&drop system. Also needed for the mouse tracking */
  256. void CHTMLReadDisplay::tdehtmlMouseMoveEvent( tdehtml::MouseMoveEvent* e ) {
  257. if( e->qmouseEvent()->state() & Qt::LeftButton == Qt::LeftButton) { //left mouse button pressed
  258. const int delay = TDEGlobalSettings::dndEventDelay();
  259. TQPoint newPos = TQPoint(e->x(), e->y());
  260. if ( (newPos.x() > m_dndData.startPos.x()+delay || newPos.x() < (m_dndData.startPos.x()-delay) ||
  261. newPos.y() > m_dndData.startPos.y()+delay || newPos.y() < (m_dndData.startPos.y()-delay)) &&
  262. !m_dndData.isDragging && m_dndData.mousePressed ) {
  263. TQDragObject* d = 0;
  264. if (!m_dndData.anchor.isEmpty() && (m_dndData.dragType == DNDData::Link) && !m_dndData.node.isNull() ) {
  265. // create a new bookmark drag!
  266. TQString module = TQString();
  267. TQString key = TQString();
  268. CReferenceManager::Type type;
  269. if ( !CReferenceManager::decodeHyperlink(m_dndData.anchor.string(), module, key, type) )
  270. return;
  271. CDragDropMgr::ItemList dndItems;
  272. //no description!
  273. dndItems.append( CDragDropMgr::Item(module, key, TQString()) );
  274. d = CDragDropMgr::dragObject(dndItems, TDEHTMLPart::view()->viewport());
  275. }
  276. else if ((m_dndData.dragType == DNDData::Text) && !m_dndData.selection.isEmpty()) {
  277. // create a new plain text drag!
  278. CDragDropMgr::ItemList dndItems;
  279. dndItems.append( CDragDropMgr::Item(m_dndData.selection) ); //no description!
  280. d = CDragDropMgr::dragObject(dndItems, TDEHTMLPart::view()->viewport());
  281. }
  282. if (d) {
  283. m_dndData.isDragging = true;
  284. m_dndData.mousePressed = false;
  285. //first make a virtual mouse click to end the selection, it it's in progress
  286. TQMouseEvent e(TQEvent::MouseButtonRelease, TQPoint(0,0), Qt::LeftButton, Qt::LeftButton);
  287. TDEApplication::sendEvent(view()->viewport(), &e);
  288. d->drag();
  289. }
  290. }
  291. }
  292. else if (getMouseTracking() && !(e->qmouseEvent()->state() & TQt::ShiftButton == TQt::ShiftButton)) {
  293. //no mouse button pressed and tracking enabled
  294. DOM::Node node = e->innerNode();
  295. //if no link was under the mouse try to find a title attribute
  296. if (!node.isNull() && (m_previousEventNode != node)) {
  297. // we want to avoid processing the node again
  298. // After some millisecs the new timer activates the Mag window update, see timerEvent()
  299. // SHIFT key not pressed, so we start timer
  300. if ( !(e->qmouseEvent()->state() & TQt::ShiftButton)) {
  301. // TQObject has simple timer
  302. killTimers();
  303. startTimer( CBTConfig::get(CBTConfig::magDelay) );
  304. }
  305. m_previousEventNode = node;
  306. }
  307. }
  308. TDEHTMLPart::tdehtmlMouseMoveEvent(e);
  309. }
  310. /** The Mag window update happens here if the mouse has not moved to another node after starting the timer.*/
  311. void CHTMLReadDisplay::timerEvent( TQTimerEvent *e ) {
  312. killTimers();
  313. DOM::Node currentNode = nodeUnderMouse();
  314. CInfoDisplay::ListInfoData infoList;
  315. // Process the node under cursor if it is the same as at the start of the timer
  316. if (!currentNode.isNull() && (currentNode != m_previousEventNode) && this->view()->hasMouse()) {
  317. DOM::Node attr;
  318. do {
  319. if (!currentNode.isNull() && (currentNode.nodeType() == DOM::Node::ELEMENT_NODE) && currentNode.hasAttributes()) { //found right node
  320. attr = currentNode.attributes().getNamedItem("note");
  321. if (!attr.isNull()) {
  322. infoList.append( tqMakePair(CInfoDisplay::Footnote, attr.nodeValue().string()) );
  323. }
  324. attr = currentNode.attributes().getNamedItem("lemma");
  325. if (!attr.isNull()) {
  326. infoList.append( tqMakePair(CInfoDisplay::Lemma, attr.nodeValue().string()) );
  327. }
  328. attr = currentNode.attributes().getNamedItem("morph");
  329. if (!attr.isNull()) {
  330. infoList.append( tqMakePair(CInfoDisplay::Morph, attr.nodeValue().string()) );
  331. }
  332. attr = currentNode.attributes().getNamedItem("expansion");
  333. if (!attr.isNull()) {
  334. infoList.append( tqMakePair(CInfoDisplay::Abbreviation, attr.nodeValue().string()) );
  335. }
  336. attr = currentNode.attributes().getNamedItem("crossrefs");
  337. if (!attr.isNull()) {
  338. infoList.append( tqMakePair(CInfoDisplay::CrossReference, attr.nodeValue().string()) );
  339. }
  340. }
  341. currentNode = currentNode.parentNode();
  342. if (!currentNode.isNull() && currentNode.hasAttributes()) {
  343. attr = currentNode.attributes().getNamedItem("class");
  344. if (!attr.isNull() && (attr.nodeValue().string() == "entry") || (attr.nodeValue().string() == "currententry") ) {
  345. break;
  346. }
  347. }
  348. }
  349. while ( !currentNode.isNull() );
  350. }
  351. // Update the mag if there is new content
  352. if (!(infoList.isEmpty())) {
  353. CPointers::infoDisplay()->setInfo(infoList);
  354. }
  355. }
  356. // ---------------------
  357. CHTMLReadDisplayView::CHTMLReadDisplayView(CHTMLReadDisplay* displayWidget, TQWidget* parent) : TDEHTMLView(displayWidget, parent), m_display(displayWidget) {
  358. viewport()->setAcceptDrops(true);
  359. setMarginWidth(4);
  360. setMarginHeight(4);
  361. };
  362. /** Opens the popupmenu at the given position. */
  363. void CHTMLReadDisplayView::popupMenu( const TQString& url, const TQPoint& pos) {
  364. if (!url.isEmpty()) {
  365. m_display->setActiveAnchor(url);
  366. }
  367. if (TQPopupMenu* popup = m_display->installedPopup()) {
  368. popup->exec(pos);
  369. }
  370. }
  371. /** Reimplementation from TQScrollView. Sets the right slots */
  372. void CHTMLReadDisplayView::polish() {
  373. TDEHTMLView::polish();
  374. connect( part(), TQT_SIGNAL(popupMenu(const TQString&, const TQPoint&)),
  375. this, TQT_SLOT(popupMenu(const TQString&, const TQPoint&)));
  376. }
  377. /** Reimplementatiob from TQScrollView. */
  378. void CHTMLReadDisplayView::contentsDropEvent( TQDropEvent* e ) {
  379. if (CDragDropMgr::canDecode(e) && CDragDropMgr::dndType(e) == CDragDropMgr::Item::Bookmark) {
  380. CDragDropMgr::ItemList dndItems = CDragDropMgr::decode(e);
  381. CDragDropMgr::Item item = dndItems.first();
  382. e->acceptAction();
  383. m_display->connectionsProxy()->emitReferenceDropped(item.bookmarkKey());
  384. return;
  385. };
  386. //don't accept the action!
  387. e->acceptAction(false);
  388. e->ignore();
  389. }
  390. /** Reimplementation from TQScrollView. */
  391. void CHTMLReadDisplayView::contentsDragEnterEvent( TQDragEnterEvent* e ) {
  392. if (CDragDropMgr::canDecode(e) && CDragDropMgr::dndType(e) == CDragDropMgr::Item::Bookmark) {
  393. e->acceptAction();
  394. return;
  395. }
  396. e->acceptAction(false);
  397. e->ignore();
  398. }
  399. /*!
  400. \fn CHTMLReadDisplay::slotPageLoaded()
  401. */
  402. void CHTMLReadDisplay::slotGoToAnchor() {
  403. if (!m_currentAnchorCache.isEmpty()) {
  404. if (!gotoAnchor( m_currentAnchorCache ) ) {
  405. tqDebug("anchor %s not present!", m_currentAnchorCache.latin1());
  406. }
  407. }
  408. m_currentAnchorCache = TQString();
  409. }
  410. void CHTMLReadDisplay::zoomIn() {
  411. setZoomFactor( (int)((float)zoomFactor()*1.1) );
  412. }
  413. void CHTMLReadDisplay::zoomOut() {
  414. setZoomFactor( (int)((float)zoomFactor()*(1.0/1.1)) );
  415. }
  416. void CHTMLReadDisplay::openFindTextDialog() {
  417. #if TDE_VERSION >= 0x030300
  418. findText();
  419. #else
  420. TQMessageBox::information(0, "Not Supported",
  421. "This copy of BibleTime was built against a version of KDE older\n"
  422. "than 3.3 (probably due to your distro), so this search feature\n"
  423. "does not work.\n\n"
  424. "This is a known issue. If we do not have a fix for the next\n"
  425. "version of BibleTime, we will remove the option.");
  426. #endif
  427. }
  428. #include "chtmlreaddisplay.moc"