fb2html.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #include "fb2html.h"
  2. #include "fb2utils.h"
  3. #include "fb2text.hpp"
  4. //---------------------------------------------------------------------------
  5. // FbTextElement::Scheme
  6. //---------------------------------------------------------------------------
  7. FbTextElement::Scheme::Scheme()
  8. {
  9. m_types["BODY"]
  10. << Type("FB:DESCRIPTION", 1, 1)
  11. << Type("FB:BODY", 1, 1)
  12. ;
  13. m_types["FB:DESCRIPTION"]
  14. << Type("FB:TITLE-INFO", 1, 1)
  15. << Type("FB:SRC-TITLE-INFO", 0, 1)
  16. << Type("FB:DOCUMENT-INFO", 1, 1)
  17. << Type("FB:PUBLISH-INFO", 0, 1)
  18. << Type("FB:CUSTOM-INFO")
  19. ;
  20. m_types["FB:DOCUMENT-INFO"]
  21. << Type("FB:AUTHOR", 1, 1)
  22. << Type("FB:PROGRAM-USED", 0, 1)
  23. << Type("FB:DATE", 0, 1)
  24. ;
  25. m_types["FB:BODY"]
  26. << Type("IMG")
  27. << Type("FB:TITLE", 0, 1)
  28. << Type("FB:EPIGRAPH")
  29. << Type("FB:SECTION", 1, 0)
  30. ;
  31. m_types["FB:SECTION"]
  32. << Type("FB:TITLE", 1, 0)
  33. << Type("FB:EPIGRAPH")
  34. << Type("IMG")
  35. << Type("FB:ANNOTATION")
  36. << Type("FB:SECTION")
  37. ;
  38. m_types["FB:POEM"]
  39. << Type("FB:TITLE", 1, 0)
  40. << Type("FB:EPIGRAPH", 0, 0)
  41. << Type("FB:STANZA", 1, 0)
  42. ;
  43. m_types["FB:STANZA"]
  44. << Type("FB:TITLE", 1, 0)
  45. ;
  46. }
  47. const FbTextElement::TypeList * FbTextElement::Scheme::operator[](const QString &name) const
  48. {
  49. TypeMap::const_iterator it = m_types.find(name);
  50. if (it != m_types.end()) return &it.value();
  51. return 0;
  52. }
  53. //---------------------------------------------------------------------------
  54. // FbTextElement::Sublist
  55. //---------------------------------------------------------------------------
  56. FbTextElement::Sublist::Sublist(const TypeList &list, const QString &name)
  57. : m_list(list)
  58. , m_pos(list.begin())
  59. {
  60. while (m_pos != list.end()) {
  61. if (m_pos->name() == name) break;
  62. ++m_pos;
  63. }
  64. }
  65. FbTextElement::Sublist::operator bool() const
  66. {
  67. return m_pos != m_list.end();
  68. }
  69. bool FbTextElement::Sublist::operator!() const
  70. {
  71. return m_pos == m_list.end();
  72. }
  73. bool FbTextElement::Sublist::operator <(const FbTextElement &element) const
  74. {
  75. if (element.isNull()) return true;
  76. const QString name = element.tagName();
  77. for (TypeList::const_iterator it = m_list.begin(); it != m_list.end(); ++it) {
  78. if (it->name() == name) return m_pos < it;
  79. }
  80. return false;
  81. }
  82. //---------------------------------------------------------------------------
  83. // FbTextElement
  84. //---------------------------------------------------------------------------
  85. FbTextElement FbTextElement::operator[](const QString &name)
  86. {
  87. QString tagName = name.toUpper();
  88. FbTextElement child = firstChild();
  89. while (!child.isNull()) {
  90. if (child.tagName() == tagName) return child;
  91. child = child.nextSibling();
  92. }
  93. return insertInside(tagName, QString("<%1></%1>").arg(name));
  94. }
  95. QString FbTextElement::nodeName() const
  96. {
  97. QString n = tagName().toLower();
  98. return n.left(3) == "fb:" ? n.mid(3) : n;
  99. }
  100. void FbTextElement::getChildren(FbElementList &list)
  101. {
  102. FbTextElement child = firstChild();
  103. while (!child.isNull()) {
  104. QString tag = child.tagName();
  105. if (tag == "FB:DESCRIPTION") {
  106. // skip description
  107. } else if (tag.left(3) == "FB:") {
  108. list << child;
  109. } else if (tag == "IMG") {
  110. list << child;
  111. } else {
  112. child.getChildren(list);
  113. }
  114. child = child.nextSibling();
  115. }
  116. }
  117. int FbTextElement::childIndex() const
  118. {
  119. FbElementList list;
  120. parent().getChildren(list);
  121. int result = 0;
  122. FbElementList::const_iterator it;
  123. for (it = list.constBegin(); it != list.constEnd(); ++it) {
  124. if (*it == *this) return result;
  125. ++result;
  126. }
  127. return -1;
  128. }
  129. bool FbTextElement::hasScheme() const
  130. {
  131. return subtypes();
  132. }
  133. const FbTextElement::TypeList * FbTextElement::subtypes() const
  134. {
  135. static Scheme scheme;
  136. return scheme[tagName()];
  137. }
  138. bool FbTextElement::hasSubtype(const QString &style) const
  139. {
  140. if (const TypeList * list = subtypes()) {
  141. for (TypeList::const_iterator item = list->begin(); item != list->end(); ++item) {
  142. if (item->name() == style) return true;
  143. }
  144. }
  145. return false;
  146. }
  147. FbTextElement::TypeList::const_iterator FbTextElement::subtype(const TypeList &list, const QString &style)
  148. {
  149. for (TypeList::const_iterator item = list.begin(); item != list.end(); ++item) {
  150. if (item->name() == style) return item;
  151. }
  152. return list.end();
  153. }
  154. FbTextElement FbTextElement::insertInside(const QString &style, const QString &html)
  155. {
  156. const TypeList * types = subtypes();
  157. if (!types) return FbTextElement();
  158. Sublist sublist(*types, style);
  159. if (sublist) {
  160. FbTextElement child = firstChild();
  161. if (sublist < child) {
  162. prependInside(html);
  163. return firstChild();
  164. }
  165. while (!child.isNull()) {
  166. FbTextElement subling = child.nextSibling();
  167. if (sublist < subling) {
  168. child.appendOutside(html);
  169. return child.nextSibling();
  170. }
  171. child = subling;
  172. }
  173. }
  174. appendInside(html);
  175. return lastChild();
  176. }
  177. QString FbTextElement::location()
  178. {
  179. return evaluateJavaScript("location(this)").toString();
  180. }
  181. void FbTextElement::select()
  182. {
  183. QString javascript = jScript("set_cursor.js");
  184. evaluateJavaScript(javascript);
  185. }
  186. bool FbTextElement::hasChild(const QString &style) const
  187. {
  188. FbTextElement child = firstChild();
  189. while (!child.isNull()) {
  190. if (child.tagName() == style) return true;
  191. child = child.nextSibling();
  192. }
  193. return false;
  194. }
  195. bool FbTextElement::isBody() const
  196. {
  197. return tagName() == "FB:BODY";
  198. }
  199. bool FbTextElement::isSection() const
  200. {
  201. return tagName() == "FB:SECTION";
  202. }
  203. bool FbTextElement::isTitle() const
  204. {
  205. return tagName() == "FB:TITLE";
  206. }
  207. bool FbTextElement::isStanza() const
  208. {
  209. return tagName() == "FB:STANZA";
  210. }
  211. bool FbTextElement::hasTitle() const
  212. {
  213. return hasChild("FB:TITLE");
  214. }
  215. int FbTextElement::index() const
  216. {
  217. int result = -1;
  218. FbTextElement prior = *this;
  219. while (!prior.isNull()) {
  220. prior = prior.previousSibling();
  221. ++result;
  222. }
  223. return result;
  224. }
  225. FbTextElement FbTextElement::child(int index) const
  226. {
  227. FbTextElement result = firstChild();
  228. while (index > 0) {
  229. result = result.nextSibling();
  230. --index;
  231. }
  232. return index ? FbTextElement() : result;
  233. }
  234. //---------------------------------------------------------------------------
  235. // FbInsertCmd
  236. //---------------------------------------------------------------------------
  237. FbInsertCmd::FbInsertCmd(const FbTextElement &element)
  238. : QUndoCommand()
  239. , m_element(element)
  240. , m_parent(element.previousSibling())
  241. , m_inner(false)
  242. {
  243. if (m_parent.isNull()) {
  244. m_parent = m_element.parent();
  245. m_inner = true;
  246. }
  247. }
  248. void FbInsertCmd::redo()
  249. {
  250. if (m_inner) {
  251. m_parent.prependInside(m_element);
  252. } else {
  253. m_parent.appendOutside(m_element);
  254. }
  255. m_element.select();
  256. }
  257. void FbInsertCmd::undo()
  258. {
  259. m_element.takeFromDocument();
  260. }
  261. //---------------------------------------------------------------------------
  262. // FbReplaceCmd
  263. //---------------------------------------------------------------------------
  264. FbReplaceCmd::FbReplaceCmd(const FbTextElement &original, const FbTextElement &duplicate)
  265. : QUndoCommand()
  266. , m_original(original)
  267. , m_duplicate(duplicate)
  268. , m_update(false)
  269. {
  270. }
  271. void FbReplaceCmd::redo()
  272. {
  273. if (m_update) {
  274. m_original.prependOutside(m_duplicate);
  275. m_original.takeFromDocument();
  276. m_duplicate.select();
  277. } else {
  278. m_update = true;
  279. }
  280. }
  281. void FbReplaceCmd::undo()
  282. {
  283. m_duplicate.prependOutside(m_original);
  284. m_duplicate.takeFromDocument();
  285. m_original.select();
  286. }
  287. //---------------------------------------------------------------------------
  288. // FbDeleteCmd
  289. //---------------------------------------------------------------------------
  290. FbDeleteCmd::FbDeleteCmd(const FbTextElement &element)
  291. : QUndoCommand()
  292. , m_element(element)
  293. , m_parent(element.previousSibling())
  294. , m_inner(false)
  295. {
  296. if (m_parent.isNull()) {
  297. m_parent = element.parent();
  298. m_inner = true;
  299. }
  300. }
  301. void FbDeleteCmd::redo()
  302. {
  303. m_element.takeFromDocument();
  304. }
  305. void FbDeleteCmd::undo()
  306. {
  307. if (m_inner) {
  308. m_parent.prependInside(m_element);
  309. } else {
  310. m_parent.appendOutside(m_element);
  311. }
  312. m_element.select();
  313. }
  314. //---------------------------------------------------------------------------
  315. // FbMoveUpCmd
  316. //---------------------------------------------------------------------------
  317. FbMoveUpCmd::FbMoveUpCmd(const FbTextElement &element)
  318. : QUndoCommand()
  319. , m_element(element)
  320. {
  321. }
  322. void FbMoveUpCmd::redo()
  323. {
  324. FbTextElement subling = m_element.previousSibling();
  325. subling.prependOutside(m_element.takeFromDocument());
  326. }
  327. void FbMoveUpCmd::undo()
  328. {
  329. FbTextElement subling = m_element.nextSibling();
  330. subling.appendOutside(m_element.takeFromDocument());
  331. }
  332. //---------------------------------------------------------------------------
  333. // FbMoveLeftCmd
  334. //---------------------------------------------------------------------------
  335. FbMoveLeftCmd::FbMoveLeftCmd(const FbTextElement &element)
  336. : QUndoCommand()
  337. , m_element(element)
  338. , m_subling(element.previousSibling())
  339. , m_parent(element.parent())
  340. {
  341. }
  342. void FbMoveLeftCmd::redo()
  343. {
  344. m_parent.appendOutside(m_element.takeFromDocument());
  345. }
  346. void FbMoveLeftCmd::undo()
  347. {
  348. if (m_subling.isNull()) {
  349. m_parent.prependInside(m_element.takeFromDocument());
  350. } else {
  351. m_subling.appendOutside(m_element.takeFromDocument());
  352. }
  353. }
  354. //---------------------------------------------------------------------------
  355. // FbMoveRightCmd
  356. //---------------------------------------------------------------------------
  357. FbMoveRightCmd::FbMoveRightCmd(const FbTextElement &element)
  358. : QUndoCommand()
  359. , m_element(element)
  360. , m_subling(element.previousSibling())
  361. {
  362. }
  363. void FbMoveRightCmd::redo()
  364. {
  365. m_subling.appendInside(m_element.takeFromDocument());
  366. }
  367. void FbMoveRightCmd::undo()
  368. {
  369. m_subling.appendOutside(m_element.takeFromDocument());
  370. }