1 #if defined(QCS_QTHTML)
2 #include "basedocument.h"
3 #include "documentpage.h"
4 #include "csoundhtmlview.h"
5 #include "ui_html5guidisplay.h"
6 #include <QLabel>
7 #include <QVBoxLayout>
8 #include <QWaitCondition>
9 #include <QFile>
10
CsoundHtmlView(QWidget * parent)11 CsoundHtmlView::CsoundHtmlView(QWidget *parent) :
12 QDockWidget(parent),
13 webView(nullptr),
14 ui(new Ui::Html5GuiDisplay),
15 documentPage(0),
16 m_csoundEngine(nullptr),
17 m_options(nullptr)
18 {
19 ui->setupUi(this);
20
21 #ifdef USE_WEBKIT
22 webView = new QWebView(this);
23 ui->inspectRow->hide(); // inspector included in QtWebKit, no need for that
24 #else
25 webView = new QWebEngineView(this);
26 //webView->page()->profile()->clearHttpCache();
27 #endif
28 csoundHtmlWrapper.setCsoundHtmlView(this);
29 csoundHtmlOnlyWrapper.setCsoundHtmlView(this);
30 ui->mainLayout->addWidget(webView); // mainLayout is vertical layout box
31 webView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
32
33 #ifdef USE_WEBKIT
34 QObject::connect(webView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()),
35 this, SLOT(addJSObject())); // to enable adding the object after reload
36 // add javascript inspector - open with right click on htmlview
37 webView->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
38 QWebInspector inspector;
39 inspector.setPage(webView->page());
40 inspector.setVisible(true);
41 #else
42 // Enable dev tools by default for the test browser
43 connect(ui->inspectButton, SIGNAL(clicked()),this, SLOT(showDebugWindow()));
44 webView->page()->setWebChannel(&channel);
45 //qDebug() << "Setting JavaScript object on init.";
46 channel.registerObject("csound", &csoundHtmlWrapper);
47 #endif
48 }
49
~CsoundHtmlView()50 CsoundHtmlView::~CsoundHtmlView()
51 {
52 delete ui;
53 }
54
getElement(const QString & text,const QString & tag)55 QString getElement(const QString &text, const QString &tag)
56 {
57 QString::SectionFlags sectionflags = QString::SectionIncludeLeadingSep | QString::SectionIncludeTrailingSep | QString::SectionCaseInsensitiveSeps;
58 QString element = text.section("<" + tag, 1, 1, sectionflags);
59 element = element.section("</" + tag + ">", 0, 0, sectionflags);
60 return element;
61 }
62
load(DocumentPage * documentPage_)63 void CsoundHtmlView::load(DocumentPage *documentPage_)
64 {
65 //TODO: call this whenever document is saved, not only on run. Usually always saved when run but there is also option not to save... Think.
66 documentPage = documentPage_; // consider rewrite...
67 qDebug() ;
68 auto text = documentPage.load()->getFullText();
69 auto filename = documentPage.load()->getFileName();
70 QFile csdfile(filename);
71 csdfile.open(QIODevice::WriteOnly);
72 QTextStream out(&csdfile);
73 out << text;
74 csdfile.close();
75 auto html = getElement(text, "html");
76 if (html.size() > 0) {
77 #ifdef USE_WEBENGINE
78 // Inject necessary code to load qtwebchannel/qwebchannel.js.
79 QString injection = R"(
80 <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
81 <script type="text/javascript">
82 "use strict";
83 document.addEventListener("DOMContentLoaded", function () {
84 try {
85 console.log("Initializing window.csound...");
86 window.channel = new QWebChannel(qt.webChannelTransport, function(channel) {
87 window.csound = channel.objects.csound;
88 if (typeof window.csound === 'undefined') {
89 alert('window.csound is undefined.');
90 return;
91 }
92 if (window.csound === null) {
93 alert('window.csound is null.');
94 return;
95 }
96 csound.message("Initialized csound.\n");
97 });
98 } catch (e) {
99 alert("initialize_csound error: " + e.message);
100 console.log(e.message);
101 }
102 });
103 </script>
104 )";
105 // Tricky because now HTML doesn't have to have a <head> element,
106 // and both <html> and <head> can have attributes. So we need to find an
107 // injection point that is the very first place allowed to put a <script>
108 // element.
109 int injection_index = html.indexOf("<head", 0, Qt::CaseInsensitive);
110 if (injection_index != -1) {
111 injection_index = html.indexOf(">", injection_index) + 1;
112 } else {
113 injection_index = html.indexOf("<html", 0, Qt::CaseInsensitive);
114 injection_index = html.indexOf(">", injection_index) + 1;
115 }
116 html = html.insert(injection_index, injection);
117 #endif
118 QString htmlfilename;
119 if (filename.startsWith(":/") ) { // an example file
120 htmlfilename = QDir::tempPath()+"/html-example.html"; // TODO: take name from filename
121 } else {
122 htmlfilename = filename + ".html";
123 }
124 QFile htmlfile(htmlfilename);
125 htmlfile.open(QIODevice::WriteOnly);
126 QTextStream out(&htmlfile);
127 out << html;
128 htmlfile.close();
129 loadFromUrl(QUrl::fromLocalFile(htmlfilename));
130 // kas aitab, kui on siin:
131 #ifdef USE_WEBENGINE
132 webView->page()->setWebChannel(&channel);
133 if (filename.endsWith(".html", Qt::CaseInsensitive)) {
134 // Register CsoundHtmlOnlyWrapper when performing HTML files.
135 qDebug() << "Setting CsoundHtmlOnlyWrapper JavaScript object on load.";
136 channel.registerObject("csound", &csoundHtmlOnlyWrapper);
137 csoundHtmlOnlyWrapper.registerConsole(documentPage_->getConsole());
138 csoundHtmlOnlyWrapper.setOptions(m_options);
139 } else {
140 // Register CsoundHtmlWrapper when performing CSD files with embedded <html> element.
141 qDebug() << "Setting CsoundWrapper JavaScript object on load.";
142 channel.registerObject("csound", &csoundHtmlWrapper);
143 }
144 #endif
145 }
146 repaint();
147 }
148
149 void CsoundHtmlView::setCsoundEngine(CsoundEngine *csEngine)
150 {
151 csoundHtmlWrapper.setCsoundEngine(csEngine);
152 }
153
154
155 void CsoundHtmlView::stop()
156 {
157 qDebug() ;
158 documentPage = 0;
159 csoundHtmlOnlyWrapper.stop();
160 }
161
162 void CsoundHtmlView::viewHtml(QString htmlText)
163 {
164 qDebug();
165 tempHtml.setFileTemplate( QDir::tempPath()+"/csoundqt-html-XXXXXX.html" ); // must have html ending for webkit
166 if (tempHtml.open()) {
167 #ifdef USE_WEBENGINE
168 // Inject necessary code to load qtwebchannel/qwebchannel.js.
169 QString injection = R"(
170 <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
171 <script>
172 "use strict";
173 document.addEventListener("DOMContentLoaded", function () {//void CsoundHtmlView::closeEvent(QCloseEvent *event)
174 //{
175 // qDebug() ;
176 // if (webView) {
177 // webView->close(); // is it necessary?
178 // }
179 //}
180
181
182
183
184 try {
185 console.log("Initializing Csound...");
186 window.channel = new QWebChannel(qt.webChannelTransport, function(channel) {
187 window.csound = channel.objects.csound;
188 csound.message("Initialized csound.\n");
189 });
190 } catch (e) {
191 alert("initialize_csound error: " + e.message);
192 console.log(e.message);
193 }
194 });
195 </script>
196 )";
197 // Tricky because now HTML doesn't have to have a <head> element,
198 // and both <html> and <head> can have attributes. So we need to find an
199 // injection point that is the very first place allowed to put a <script>
200 // element.
201 int injection_index = htmlText.indexOf("<head", 0, Qt::CaseInsensitive);
202 if (injection_index != -1) {
203 injection_index = htmlText.indexOf(">", injection_index) + 1;
204 } else {
205 injection_index = htmlText.indexOf("<html", 0, Qt::CaseInsensitive);
206 injection_index = htmlText.indexOf(">", injection_index) + 1;
207 }
208 htmlText = htmlText.insert(injection_index, injection);
209 #endif
210 tempHtml.write(htmlText.toLocal8Bit());
211 tempHtml.resize(tempHtml.pos()); // otherwise may keep contents from previous write if that was bigger
212 tempHtml.close();
213 loadFromUrl(QUrl::fromLocalFile(tempHtml.fileName()));
214 #ifdef USE_WEBENGINE
215 webView->page()->setWebChannel(&channel);
216 auto filename = documentPage.load()->getFileName();
217 if (filename.endsWith(".html", Qt::CaseInsensitive)) {
218 // Register CsoundHtmlOnlyWrapper when performing HTML files.
219 qDebug() << "Setting CsoundHtmlOnlyWrapper JavaScript object on view.";
220 channel.registerObject("csound", &csoundHtmlOnlyWrapper);
221 } else {
222 // Register CsoundHtmlWrapper when performing CSD files with embedded <html> element.
223 qDebug() << "Setting CsoundWrapper JavaScript object on view.";
224 channel.registerObject("csound", &csoundHtmlWrapper);
225 }
226 #endif
227 }
228 }
229
230 #ifdef USE_WEBKIT
231 void CsoundHtmlView::addJSObject()
232 {
233 if (webView) {
234 QString filename = documentPage.load()->getFileName();
235 qDebug()<<"Adding Csound as JavaScript object";
236 if (filename.endsWith(".html", Qt::CaseInsensitive)) {
237 // Register CsoundHtmlOnlyWrapper when performing HTML files.
238 webView->page()->mainFrame()->addToJavaScriptWindowObject("csound", &csoundHtmlOnlyWrapper);
239 csoundHtmlOnlyWrapper.registerConsole(documentPage.load()->getConsole());
240 csoundHtmlOnlyWrapper.setOptions(m_options);
241 } else {
242 // Register CsoundHtmlWrapper when performing CSD files with embedded <html> element.
243 webView->page()->mainFrame()->addToJavaScriptWindowObject("csound", &csoundHtmlWrapper);
244 }
245
246
247 }
248
249
250
251 }
252 #endif
253
254 void CsoundHtmlView::loadFromUrl(const QUrl &url)
255 {
256 qDebug();
257
258 if(webView != 0) {
259 webView->setUrl(url);
260 }
261 if (!this->isVisible()) {
262 this->show();
263 this->raise();
264 }
265 }
266
267 void CsoundHtmlView::clear()
268 {
269 this->hide(); // just hide the panel, keep qwebchannel and other connections
270 //loadFromUrl(QUrl()); // empty URL to clear - this causes "qt is not defined" error
271 }
272
273 void CsoundHtmlView::setOptions(CsoundOptions *options)
274 {
275 m_options = options;
276 }
277
278 #ifdef USE_WEBENGINE
279 void CsoundHtmlView::showDebugWindow()
280 {
281 qDebug();
282 QByteArray debugPort = qgetenv("QTWEBENGINE_REMOTE_DEBUGGING");
283 if (!debugPort.isNull()) {
284 QWidget * debugger = new QWidget();
285 debugger->resize(600,400);
286 QWebEngineView * debuggerView= new QWebEngineView(debugger);
287 QVBoxLayout *layout = new QVBoxLayout(debugger);
288 layout->addWidget(debuggerView);
289 debugger->setAttribute(Qt::WA_DeleteOnClose);
290 debugger->setLayout(layout);
291 qDebug()<<"Opening window for localhost:"<<debugPort;
292 debuggerView->setUrl(QUrl("http://localhost:"+debugPort));
293 debugger->show();
294 } else {
295 qDebug()<<"Debugging port not set or reading failed";
296 }
297 }
298
299 #endif
300 #endif
301