fb2page.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #include "fb2page.hpp"
  2. #include <QTimer>
  3. #include <QWebFrame>
  4. #include <QtDebug>
  5. #include "fb2read.hpp"
  6. #include "fb2save.hpp"
  7. #include "fb2temp.hpp"
  8. #include "fb2utils.h"
  9. #include "fb2html.h"
  10. #include "fb2xml2.h"
  11. //---------------------------------------------------------------------------
  12. // FbTextLogger
  13. //---------------------------------------------------------------------------
  14. void FbTextLogger::trace(const QString &text)
  15. {
  16. qCritical() << text;
  17. }
  18. //---------------------------------------------------------------------------
  19. // FbTextPage
  20. //---------------------------------------------------------------------------
  21. FbTextPage::FbTextPage(QObject *parent)
  22. : QWebPage(parent)
  23. , m_logger(this)
  24. {
  25. QWebSettings *s = settings();
  26. s->setAttribute(QWebSettings::AutoLoadImages, true);
  27. s->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
  28. s->setAttribute(QWebSettings::JavaEnabled, false);
  29. s->setAttribute(QWebSettings::JavascriptEnabled, true);
  30. s->setAttribute(QWebSettings::PrivateBrowsingEnabled, true);
  31. s->setAttribute(QWebSettings::PluginsEnabled, false);
  32. s->setAttribute(QWebSettings::ZoomTextOnly, true);
  33. s->setUserStyleSheetUrl(QUrl::fromLocalFile(":style.css"));
  34. QString html = block("body", block("section", p()));
  35. mainFrame()->setHtml(html, createUrl());
  36. setContentEditable(true);
  37. setNetworkAccessManager(new FbNetworkAccessManager(this));
  38. connect(this, SIGNAL(linkHovered(QString,QString,QString)), parent, SLOT(linkHovered(QString,QString,QString)));
  39. connect(this, SIGNAL(loadFinished(bool)), SLOT(loadFinished()));
  40. connect(this, SIGNAL(contentsChanged()), SLOT(fixContents()));
  41. QFile file(":blank.fb2");
  42. if (file.open(QFile::ReadOnly | QFile::Text)) {
  43. read(file);
  44. }
  45. }
  46. FbNetworkAccessManager *FbTextPage::temp()
  47. {
  48. return qobject_cast<FbNetworkAccessManager*>(networkAccessManager());
  49. }
  50. bool FbTextPage::read(const QString &html)
  51. {
  52. QXmlInputSource *source = new QXmlInputSource();
  53. source->setData(html);
  54. FbReadThread::execute(this, source);
  55. return true;
  56. }
  57. bool FbTextPage::read(QIODevice &device)
  58. {
  59. QXmlInputSource *source = new QXmlInputSource();
  60. source->setData(device.readAll());
  61. FbReadThread::execute(this, source);
  62. return true;
  63. }
  64. void FbTextPage::html(QObject *temp, const QString &html)
  65. {
  66. FbNetworkAccessManager *manager = qobject_cast<FbNetworkAccessManager*>(temp);
  67. if (!manager) { temp->deleteLater(); return; }
  68. QUrl url = FbTextPage::createUrl();
  69. setNetworkAccessManager(manager);
  70. manager->setPath(url.path());
  71. manager->setParent(this);
  72. QWebSettings::clearMemoryCaches();
  73. mainFrame()->setHtml(html, url);
  74. }
  75. bool FbTextPage::acceptNavigationRequest(QWebFrame *frame, const QNetworkRequest &request, NavigationType type)
  76. {
  77. Q_UNUSED(frame);
  78. if (type == NavigationTypeLinkClicked) {
  79. qCritical() << request.url().fragment();
  80. return false;
  81. }
  82. return QWebPage::acceptNavigationRequest(frame, request, type);
  83. }
  84. QUrl FbTextPage::createUrl()
  85. {
  86. static int number = 0;
  87. return QString("fb2:/%1/").arg(number++);
  88. }
  89. QString FbTextPage::block(const QString &name)
  90. {
  91. return block(name, p());
  92. }
  93. QString FbTextPage::block(const QString &name, const QString &text)
  94. {
  95. return QString("<fb:%1>%2</fb:%1>").arg(name).arg(text);
  96. }
  97. QString FbTextPage::p(const QString &text)
  98. {
  99. return QString("<p>%1</p>").arg(text);
  100. }
  101. FbTextElement FbTextPage::body()
  102. {
  103. return doc().findFirst("body");
  104. }
  105. FbTextElement FbTextPage::doc()
  106. {
  107. return mainFrame()->documentElement();
  108. }
  109. void FbTextPage::push(QUndoCommand * command, const QString &text)
  110. {
  111. undoStack()->beginMacro(text);
  112. undoStack()->push(command);
  113. undoStack()->endMacro();
  114. }
  115. void FbTextPage::update()
  116. {
  117. emit contentsChanged();
  118. emit selectionChanged();
  119. }
  120. FbTextElement FbTextPage::appendSection(const FbTextElement &parent)
  121. {
  122. QString html = block("section", block("title", p()) + p());
  123. FbTextElement element = parent;
  124. element.appendInside(html);
  125. element = parent.lastChild();
  126. QUndoCommand * command = new FbInsertCmd(element);
  127. push(command, tr("Append section"));
  128. return element;
  129. }
  130. FbTextElement FbTextPage::appendTitle(const FbTextElement &parent)
  131. {
  132. QString html = block("title", p());
  133. FbTextElement element = parent;
  134. element.prependInside(html);
  135. element = parent.firstChild();
  136. QUndoCommand * command = new FbInsertCmd(element);
  137. push(command, tr("Append section"));
  138. return element;
  139. }
  140. FbTextElement FbTextPage::appendText(const FbTextElement &parent)
  141. {
  142. FbTextElement element = parent;
  143. element.appendInside(p());
  144. return element.lastChild();
  145. }
  146. void FbTextPage::insertBody()
  147. {
  148. QString html = block("body", block("title", p()) + block("section", block("title", p()) + p()));
  149. FbTextElement element = body();
  150. element.appendInside(html);
  151. element = element.lastChild();
  152. QUndoCommand * command = new FbInsertCmd(element);
  153. push(command, tr("Append body"));
  154. }
  155. void FbTextPage::insertSection()
  156. {
  157. FbTextElement element = current();
  158. while (!element.isNull()) {
  159. if (element.isSection() || element.isBody()) {
  160. appendSection(element);
  161. break;
  162. }
  163. element = element.parent();
  164. }
  165. }
  166. void FbTextPage::insertTitle()
  167. {
  168. FbTextElement element = current();
  169. while (!element.isNull()) {
  170. FbTextElement parent = element.parent();
  171. if ((parent.isSection() || parent.isBody()) && !parent.hasTitle()) {
  172. QString html = block("title", p());
  173. parent.prependInside(html);
  174. element = parent.firstChild();
  175. QUndoCommand * command = new FbInsertCmd(element);
  176. push(command, tr("Insert title"));
  177. break;
  178. }
  179. element = parent;
  180. }
  181. }
  182. void FbTextPage::insertSubtitle()
  183. {
  184. FbTextElement element = current();
  185. while (!element.isNull()) {
  186. FbTextElement parent = element.parent();
  187. if (parent.isSection()) {
  188. QString html = block("subtitle", p());
  189. if (element.isTitle()) {
  190. element.appendOutside(html);
  191. element = element.nextSibling();
  192. } else {
  193. element.prependOutside(html);
  194. element = element.previousSibling();
  195. }
  196. QUndoCommand * command = new FbInsertCmd(element);
  197. push(command, tr("Insert subtitle"));
  198. break;
  199. }
  200. element = parent;
  201. }
  202. }
  203. void FbTextPage::insertPoem()
  204. {
  205. FbTextElement element = current();
  206. while (!element.isNull()) {
  207. FbTextElement parent = element.parent();
  208. if (parent.isSection()) {
  209. QString html = block("poem", block("stanza", p()));
  210. if (element.isTitle()) {
  211. element.appendOutside(html);
  212. element = element.nextSibling();
  213. } else {
  214. element.prependOutside(html);
  215. element = element.previousSibling();
  216. }
  217. QUndoCommand * command = new FbInsertCmd(element);
  218. push(command, tr("Insert poem"));
  219. break;
  220. }
  221. element = parent;
  222. }
  223. }
  224. void FbTextPage::insertStanza()
  225. {
  226. FbTextElement element = current();
  227. while (!element.isNull()) {
  228. if (element.isStanza()) {
  229. QString html = block("stanza", p());
  230. element.appendOutside(html);
  231. element = element.nextSibling();
  232. QUndoCommand * command = new FbInsertCmd(element);
  233. push(command, tr("Append stanza"));
  234. break;
  235. }
  236. element = element.parent();
  237. }
  238. }
  239. void FbTextPage::insertAnnot()
  240. {
  241. }
  242. void FbTextPage::insertAuthor()
  243. {
  244. }
  245. void FbTextPage::insertEpigraph()
  246. {
  247. const QString type = "epigraph";
  248. FbTextElement element = current();
  249. while (!element.isNull()) {
  250. if (element.hasSubtype(type)) {
  251. QString html = block("epigraph", p());
  252. element = element.insertInside(type, html);
  253. QUndoCommand * command = new FbInsertCmd(element);
  254. push(command, tr("Insert epigraph"));
  255. break;
  256. }
  257. element = element.parent();
  258. }
  259. }
  260. void FbTextPage::insertDate()
  261. {
  262. }
  263. void FbTextPage::insertText()
  264. {
  265. }
  266. void FbTextPage::createBlock(const QString &name)
  267. {
  268. QString style = name;
  269. QString js1 = jScript("section_get.js");
  270. QString result = mainFrame()->evaluateJavaScript(js1).toString();
  271. QStringList list = result.split("|");
  272. if (list.count() < 2) return;
  273. const QString location = list[0];
  274. const QString position = list[1];
  275. if (style == "title" && position.left(2) != "0,") style.prepend("sub");
  276. FbTextElement original = element(location);
  277. FbTextElement duplicate = original.clone();
  278. original.appendOutside(duplicate);
  279. original.takeFromDocument();
  280. QString js2 = jScript("section_new.js") + ";f(this,'fb:%1',%2)";
  281. duplicate.evaluateJavaScript(js2.arg(style).arg(position));
  282. QUndoCommand * command = new FbReplaceCmd(original, duplicate);
  283. push(command, tr("Create <%1>").arg(style));
  284. }
  285. void FbTextPage::createSection()
  286. {
  287. createBlock("section");
  288. }
  289. void FbTextPage::deleteSection()
  290. {
  291. FbTextElement element = current();
  292. while (!element.isNull()) {
  293. if (element.isSection()) {
  294. if (element.parent().isBody()) return;
  295. FbTextElement original = element.parent();
  296. FbTextElement duplicate = original.clone();
  297. int index = element.index();
  298. original.appendOutside(duplicate);
  299. original.takeFromDocument();
  300. element = duplicate.child(index);
  301. if (index) {
  302. FbTextElement title = element.firstChild();
  303. if (title.isTitle()) {
  304. title.removeClass("title");
  305. title.addClass("subtitle");
  306. }
  307. }
  308. QString xml = element.toInnerXml();
  309. element.setOuterXml(xml);
  310. QUndoCommand * command = new FbReplaceCmd(original, duplicate);
  311. push(command, tr("Remove section"));
  312. element.select();
  313. break;
  314. }
  315. element = element.parent();
  316. }
  317. }
  318. void FbTextPage::createTitle()
  319. {
  320. createBlock("title");
  321. }
  322. FbTextElement FbTextPage::current()
  323. {
  324. return element(location());
  325. }
  326. FbTextElement FbTextPage::element(const QString &location)
  327. {
  328. if (location.isEmpty()) return FbTextElement();
  329. QStringList list = location.split(",", QString::SkipEmptyParts);
  330. QStringListIterator iterator(list);
  331. QWebElement result = doc();
  332. while (iterator.hasNext()) {
  333. QString str = iterator.next();
  334. int pos = str.indexOf("=");
  335. QString tag = str.left(pos);
  336. int key = str.mid(pos + 1).toInt();
  337. if (key < 0) break;
  338. result = result.firstChild();
  339. while (0 < key--) result = result.nextSibling();
  340. }
  341. return result;
  342. }
  343. QString FbTextPage::location()
  344. {
  345. QString javascript = "location(document.getSelection().anchorNode)";
  346. return mainFrame()->evaluateJavaScript(javascript).toString();
  347. }
  348. QString FbTextPage::status()
  349. {
  350. QString javascript = jScript("get_status.js");
  351. QString status = mainFrame()->evaluateJavaScript(javascript).toString();
  352. return status.replace("FB:", "");
  353. }
  354. void FbTextPage::loadFinished()
  355. {
  356. mainFrame()->addToJavaScriptWindowObject("logger", &m_logger);
  357. FbTextElement element = body().findFirst("fb\\:section");
  358. if (element.isNull()) element = body().findFirst("fb\\:body");
  359. if (element.isNull()) element = body();
  360. FbTextElement child = element.firstChild();
  361. if (child.isTitle()) child = child.nextSibling();
  362. if (!child.isNull()) element = child;
  363. element.select();
  364. }
  365. void FbTextPage::fixContents()
  366. {
  367. foreach (QWebElement span, doc().findAll("span.apple-style-span[style]")) {
  368. span.removeAttribute("style");
  369. }
  370. foreach (QWebElement span, doc().findAll("[style]")) {
  371. span.removeAttribute("style");
  372. }
  373. }