Typica is a free program for professional coffee roasters. https://typica.us
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.

webview.w 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. @* Web Views in Typica.
  2. \noindent Typica makes extensive use of web views. All printing is currently
  3. handled by generating HTML with the required information, loading it into a web
  4. view, and using the print capabilities provided there. Reports are provided by
  5. running SQL queries, generating HTML, and displaying the results in a web view.
  6. Even the program'@q'@>s about window is primarily a web view.
  7. In order to simplify the implementation of certain features, we subclass
  8. |QWebView| and provide some additional functionality.
  9. @(webview.h@>=
  10. #include <QWebView>
  11. #include <QFile>
  12. #include <QMessageBox>
  13. #include <QDesktopServices>
  14. #include <QPrinter>
  15. #include <QPrintDialog>
  16. #include <QWebFrame>
  17. #include <QWebElement>
  18. #ifndef TypicaWebViewHeader
  19. #define TypicaWebViewHeader
  20. class TypicaWebView : public QWebView
  21. {
  22. Q_OBJECT
  23. public:
  24. TypicaWebView();
  25. Q_INVOKABLE void load(const QString &url);
  26. Q_INVOKABLE void print();
  27. Q_INVOKABLE void setHtml(const QString &html, const QUrl &baseUrl = QUrl());
  28. Q_INVOKABLE void setContent(QIODevice *device);
  29. Q_INVOKABLE QString saveXml();
  30. Q_INVOKABLE QWebElement documentElement();
  31. Q_INVOKABLE QWebElement findFirstElement(const QString &selector);
  32. signals:
  33. void scriptLinkClicked(const QString &link);
  34. private slots:
  35. void linkDelegate(const QUrl &url);
  36. };
  37. #endif
  38. @ The implementation is in a separate file.
  39. @(webview.cpp@>=
  40. #include "webview.h"
  41. @<TypicaWebView implementation@>@;
  42. @ In the constructor we set up our link delegation policy.
  43. @<TypicaWebView implementation@>=
  44. TypicaWebView::TypicaWebView() : QWebView()
  45. {
  46. page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
  47. connect(page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkDelegate(QUrl)));
  48. }
  49. @ When a link is clicked, one of three things may happen. Certain reserved URLs
  50. will trigger an immediate event which is just handled internally without
  51. providing any kind of external notification. URLs that start with
  52. |"typica://script/"| will produce a signal containing the rest of the URL. This
  53. is intended for script code to intercept, interpret, and update the web view
  54. with the requested information. Everything else is passed to |QDesktopServices|
  55. which will defer to system-wide preferences for how to handle different link
  56. and file types.
  57. @<TypicaWebView implementation@>=
  58. void TypicaWebView::linkDelegate(const QUrl &url)
  59. {
  60. if(url.scheme() == "typica")
  61. {
  62. QString address(url.toEncoded());
  63. @<Detect and handle special links@>@;
  64. @<Detect and handle script links@>@;
  65. }
  66. else
  67. {
  68. QDesktopServices::openUrl(url);
  69. }
  70. }
  71. @ Currently the only special link is |"typica://aboutqt"| which brings up
  72. information about the Qt framework. This is used in the About Typica window.
  73. @<Detect and handle special links@>=
  74. if(address == "typica://aboutqt")
  75. {
  76. QMessageBox::aboutQt(this);
  77. return;
  78. }
  79. @ Script links split the link data to simplify interpretation in script code.
  80. @<Detect and handle script links@>=
  81. if(address.startsWith("typica://script/"))
  82. {
  83. emit scriptLinkClicked(address.remove(0, 16));
  84. return;
  85. }
  86. @ There is a limited set of functions that should be available to the host
  87. environment which are not declared as slots or otherwise have some missing
  88. functionality in the base class. In some cases the new functions can be
  89. distinguished by signature.
  90. @<TypicaWebView implementation@>=
  91. void TypicaWebView::load(const QString &url)
  92. {
  93. QWebView::load(QUrl(url));
  94. }
  95. void TypicaWebView::print()
  96. {
  97. QPrinter *printer = new QPrinter(QPrinter::HighResolution);
  98. QPrintDialog printDialog(printer, NULL);
  99. if(printDialog.exec() == QDialog::Accepted)
  100. {
  101. QWebView::print(printer);
  102. }
  103. }
  104. void TypicaWebView::setHtml(const QString &html, const QUrl &baseUrl)
  105. {
  106. QWebView::setHtml(html, baseUrl);
  107. }
  108. void TypicaWebView::setContent(QIODevice *device)
  109. {
  110. device->reset();
  111. QByteArray content = device->readAll();
  112. QWebView::setContent(content, "application/xhtml+xml");
  113. }
  114. QString TypicaWebView::saveXml()
  115. {
  116. return page()->currentFrame()->documentElement().toOuterXml();
  117. }
  118. @ Web views are exposed to the host environment in the usual manner.
  119. @<Set up the scripting engine@>=
  120. constructor = engine->newFunction(constructWebView);
  121. value = engine->newQMetaObject(&TypicaWebView::staticMetaObject, constructor);
  122. engine->globalObject().setProperty("WebView", value);
  123. @ Now that |QWebView| is subclassed, all of the features that we need should
  124. be available through the meta-object system automatically.
  125. @<Functions for scripting@>=
  126. QScriptValue constructWebView(QScriptContext *, QScriptEngine *engine)
  127. {
  128. QScriptValue object = engine->newQObject(new TypicaWebView);
  129. setQWebViewProperties(object, engine);
  130. return object;
  131. }
  132. void setQWebViewProperties(QScriptValue value, QScriptEngine *engine)
  133. {
  134. setQWidgetProperties(value, engine);
  135. }
  136. @ It is also possible to create these web views from the XML portion of the
  137. configuration document. A function is provided to add a new view to a box
  138. layout.
  139. @<Functions for scripting@>=
  140. void addWebViewToLayout(QDomElement element, QStack<QWidget *> *,
  141. QStack<QLayout *> *layoutStack)
  142. {
  143. TypicaWebView *view = new TypicaWebView;
  144. if(element.hasAttribute("id"))
  145. {
  146. view->setObjectName(element.attribute("id"));
  147. }
  148. QBoxLayout *layout = qobject_cast<QBoxLayout *>(layoutStack->top());
  149. layout->addWidget(view);
  150. }
  151. @ Prototypes must be provided for these functions.
  152. @<Function prototypes for scripting@>=
  153. QScriptValue constructWebView(QScriptContext *context, QScriptEngine *engine);
  154. void setQWebViewProperties(QScriptValue value, QScriptEngine *engine);
  155. void addWebViewToLayout(QDomElement element, QStack<QWidget *> *widgetStack,
  156. QStack<QLayout *> *layoutStack);
  157. @ Finally, we must include our new header in |"typica.cpp"|.
  158. @<Header files to include@>=
  159. #include "webview.h"
  160. @* Web View DOM Access.
  161. \noindent Two methods in |TypicaWebView| provide access to the DOM for the
  162. currently displayed page. These are simple wrappers. The |QWebElement| is also
  163. @<TypicaWebView implementation@>=
  164. QWebElement TypicaWebView::documentElement()
  165. {
  166. return page()->mainFrame()->documentElement();
  167. }
  168. QWebElement TypicaWebView::findFirstElement(const QString &selector)
  169. {
  170. return page()->mainFrame()->findFirstElement(selector);
  171. }
  172. @ In order to call these methods we need to be able to convert a |QWebElement|
  173. to and from a |QScriptValue|.
  174. @<Function prototypes for scripting@>=
  175. QScriptValue QWebElement_toScriptValue(QScriptEngine *engine, const QWebElement &element);
  176. void QWebElement_fromScriptValue(const QScriptValue &value, QWebElement &element);
  177. @ The implementation simply packs these in a variant.
  178. @<Functions for scripting@>=
  179. QScriptValue QWebElement_toScriptValue(QScriptEngine *engine, const QWebElement &element)
  180. {
  181. QVariant var;
  182. var.setValue(element);
  183. QScriptValue object = engine->newVariant(var);
  184. return object;
  185. }
  186. void QWebElement_fromScriptValue(const QScriptValue &value, QWebElement &element)
  187. {
  188. element = value.toVariant().value<QWebElement>();
  189. }
  190. @ These methods must be registered with the engine.
  191. @<Set up the scripting engine@>=
  192. qScriptRegisterMetaType(engine, QWebElement_toScriptValue, QWebElement_fromScriptValue);
  193. @ As |QWebElement| by itself is not well suited for scripting, we must provide
  194. a way to attach useful properties to the returned object. We do this with a
  195. simple wrapper class.
  196. @(webelement.h@>=
  197. #include <QWebElement>
  198. #include <QObject>
  199. #ifndef TypicaWebElementHeader
  200. #define TypicaWebElementHeader
  201. class TypicaWebElement : public QObject
  202. {
  203. Q_OBJECT
  204. public:
  205. TypicaWebElement(QWebElement element);
  206. Q_INVOKABLE void appendInside(const QString &markup);
  207. Q_INVOKABLE void appendOutside(const QString &markup);
  208. Q_INVOKABLE void prependInside(const QString &markup);
  209. Q_INVOKABLE void prependOutside(const QString &markup);
  210. Q_INVOKABLE void removeFromDocument();
  211. Q_INVOKABLE void replace(const QString &markup);
  212. Q_INVOKABLE void setInnerXml(const QString &markup);
  213. Q_INVOKABLE void setOuterXml(const QString &markup);
  214. Q_INVOKABLE void setPlainText(const QString &text);
  215. private:
  216. QWebElement e;
  217. };
  218. #endif
  219. @ A constructor is required for creating this wrapper for the host environment.
  220. @<Function prototypes for scripting@>=
  221. QScriptValue constructWebElement(QScriptContext *context,
  222. QScriptEngine *engine);
  223. @ The script engine is informed of this function.
  224. @<Set up the scripting engine@>=
  225. constructor = engine->newFunction(constructWebElement);
  226. engine->globalObject().setProperty("WebElement", constructor);
  227. @ A specialization of the |argument()| template method is required to
  228. pass the |QWebElement| through to the wrapper constructor.
  229. @<Functions for scripting@>=
  230. template<> QWebElement argument(int arg, QScriptContext *context)
  231. {
  232. return qscriptvalue_cast<QWebElement>(context->argument(arg));
  233. }
  234. @ Our wrapper constructor takes a single argument which is the |QWebElement| to
  235. wrap.
  236. @<Functions for scripting@>=
  237. QScriptValue constructWebElement(QScriptContext *context,
  238. QScriptEngine *engine)
  239. {
  240. QWebElement element = argument<QWebElement>(0, context);
  241. QScriptValue object = engine->newQObject(new TypicaWebElement(element));
  242. return object;
  243. }
  244. @ The |TypicaWebElement| constructor just keeps a copy of the element passed as
  245. an argument.
  246. @<TypicaWebElement implementation@>=
  247. TypicaWebElement::TypicaWebElement(QWebElement element) : e(element)
  248. {
  249. /* Nothing needs to be done here. */
  250. }
  251. @ Everything else is a simple wrapper around the equivalent |QWebElement|
  252. method.
  253. @<TypicaWebElement implementation@>=
  254. void TypicaWebElement::appendInside(const QString &markup)
  255. {
  256. e.appendInside(markup);
  257. }
  258. void TypicaWebElement::appendOutside(const QString &markup)
  259. {
  260. e.appendOutside(markup);
  261. }
  262. void TypicaWebElement::prependInside(const QString &markup)
  263. {
  264. e.prependInside(markup);
  265. }
  266. void TypicaWebElement::prependOutside(const QString &markup)
  267. {
  268. e.prependOutside(markup);
  269. }
  270. void TypicaWebElement::removeFromDocument()
  271. {
  272. e.removeFromDocument();
  273. }
  274. void TypicaWebElement::replace(const QString &markup)
  275. {
  276. e.replace(markup);
  277. }
  278. void TypicaWebElement::setInnerXml(const QString &markup)
  279. {
  280. e.setInnerXml(markup);
  281. }
  282. void TypicaWebElement::setOuterXml(const QString &markup)
  283. {
  284. e.setOuterXml(markup);
  285. }
  286. void TypicaWebElement::setPlainText(const QString &text)
  287. {
  288. e.setPlainText(text);
  289. }
  290. @ Implementation is in a separate file.
  291. @(webelement.cpp@>=
  292. #include "webelement.h"
  293. @<TypicaWebElement implementation@>@;
  294. @ Another header is required.
  295. @<Header files to include@>=
  296. #include "webelement.h"