MythTV master
mythuiwebbrowser.cpp
Go to the documentation of this file.
1
7#include "mythuiwebbrowser.h"
8
9// c++
10#include <algorithm>
11#include <chrono> // for milliseconds
12#include <thread> // for sleep_for
13
14// qt
15#include <QApplication>
16#include <QWebEngineHistory>
17#include <QWebEngineSettings>
18#include <QWebEngineScript>
19#include <QWebEngineScriptCollection>
20#include <QPainter>
21#include <QDir>
22#include <QStyle>
23#include <QKeyEvent>
24#include <QDomDocument>
25#include <QDebug>
26
27// libmythbase
29#include "libmythbase/mythdb.h"
33
34//libmythui
35#include "mythpainter.h"
36#include "mythimage.h"
37#include "mythmainwindow.h"
38#include "mythfontproperties.h"
39#include "mythuihelper.h"
40#include "mythdialogbox.h"
41#include "mythprogressdialog.h"
42#include "mythuiscrollbar.h"
43#include <qlogging.h>
44
46{
47 QString m_mimeType;
48 QString m_extension;
50};
51
52static const std::vector<MimeType> SupportedMimeTypes
53{
54 { "audio/mpeg3", "mp3", false },
55 { "audio/x-mpeg-3", "mp3", false },
56 { "audio/mpeg", "mp2", false },
57 { "audio/x-mpeg", "mp2", false },
58 { "audio/ogg", "ogg", false },
59 { "audio/ogg", "oga", false },
60 { "audio/flac", "flac", false },
61 { "audio/x-ms-wma", "wma", false },
62 { "audio/wav", "wav", false },
63 { "audio/x-wav", "wav", false },
64 { "audio/ac3", "ac3", false },
65 { "audio/x-ac3", "ac3", false },
66 { "audio/x-oma", "oma", false },
67 { "audio/x-realaudio", "ra", false },
68 { "audio/dts", "dts", false },
69 { "audio/x-dts", "dts", false },
70 { "audio/aac", "aac", false },
71 { "audio/x-aac", "aac", false },
72 { "audio/m4a", "m4a", false },
73 { "audio/x-m4a", "m4a", false },
74 { "video/mpeg", "mpg", true },
75 { "video/mpeg", "mpeg", true },
76 { "video/x-ms-wmv", "wmv", true },
77 { "video/x-ms-wmv", "avi", true },
78 { "application/x-troff-msvideo", "avi", true },
79 { "video/avi", "avi", true },
80 { "video/msvideo", "avi", true },
81 { "video/x-msvideo", "avi", true }
82};
83
84
91 : QWebEngineView(parent), m_parentBrowser(parentBrowser)
92{
93 m_profile = new QWebEngineProfile("MythTV", this);
94 //m_profile->setHttpUserAgent("Mozilla/5.0 (SMART-TV; Linux; Tizen 5.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/5.0 NativeTVAds Safari/538.1");
95 m_profile->setPersistentCookiesPolicy(QWebEngineProfile::AllowPersistentCookies);
96
97 m_webpage = new QWebEnginePage(m_profile, this);
98
99 setPage(m_webpage);
100
101 installEventFilter(this);
102}
103
105{
106 delete m_webpage;
107 delete m_profile;
108}
109
110bool MythWebEngineView::eventFilter(QObject *obj, QEvent *event)
111{
112 if (event->type() == QEvent::ShortcutOverride)
113 {
114 // intercept all key presses
115 auto *keyEvent = dynamic_cast<QKeyEvent *>(event);
116 if (keyEvent == nullptr)
117 {
118 LOG(VB_GENERAL, LOG_ALERT,
119 "MythWebEngineView::eventFilter() couldn't cast event");
120 return true;
121 }
122
123 bool res = handleKeyPress(keyEvent);
124 if (!res)
125 keyEvent->accept();
126 else
127 keyEvent->ignore();
128
129 return false; // clazy:exclude=base-class-event
130 }
131
132 // standard event processing
133 return QWebEngineView::eventFilter(obj, event);
134}
135
136void MythWebEngineView::sendKeyPress(int key, Qt::KeyboardModifiers modifiers, const QString &text)
137{
138 Q_FOREACH(QObject* obj, children())
139 {
140 if (qobject_cast<QWidget*>(obj))
141 {
142 auto *event = new QKeyEvent(QEvent::KeyPress, key, modifiers, text, false, 1);
143 QCoreApplication::postEvent(obj, event);
144 }
145 }
146}
147
149{
150 QStringList actions;
151 bool handled = true;
152 handled = GetMythMainWindow()->TranslateKeyPress("Browser", event, actions);
153
154 for (int i = 0; i < actions.size() && !handled; i++)
155 {
156 const QString& action = actions[i];
157 handled = true;
158
159 if (action == "ESCAPE")
160 {
161 if (history()->canGoBack())
162 back();
163 else
164 {
166 handled = true;
167 }
168 }
169 else if (action == "NEXTLINK")
170 {
171 if (event->key() != Qt::Key_Tab)
172 {
173 sendKeyPress(Qt::Key_Tab, Qt::NoModifier, QString('\t'));
174 return true;
175 }
176
177 handled = false;
178 }
179 else if (action == "PREVIOUSLINK")
180 {
181 if (event->key() != Qt::Key_Tab)
182 {
183 sendKeyPress(Qt::Key_Tab, Qt::ShiftModifier, QString('\t'));
184 return true;
185 }
186
187 handled = false;
188 }
189 else if (action == "FOLLOWLINK")
190 {
191 if (event->key() != Qt::Key_Return)
192 {
193 sendKeyPress(Qt::Key_Return, Qt::NoModifier, QString('\r'));
194 return true;
195 }
196
197 handled = false;
198 }
199 else if (action == "TOGGLEINPUT")
200 {
202 {
204 m_parentBrowser->SendStatusBarMessage(tr("Sending key presses to MythTV"));
205 }
206 else
207 {
209 m_parentBrowser->SendStatusBarMessage(tr("Sending key presses to web page"));
210 }
211 }
212 else if (action == "ZOOMIN")
213 {
215 }
216 else if (action == "ZOOMOUT")
217 {
219 }
220 else if (action == "RELOAD")
221 {
222 m_parentBrowser->Reload(true);
223 }
224 else if (action == "FULLRELOAD")
225 {
226 m_parentBrowser->Reload(false);
227 }
228 else if (action == "MOUSEUP" || action == "MOUSEDOWN" ||
229 action == "MOUSELEFT" || action == "MOUSERIGHT" ||
230 action == "MOUSELEFTBUTTON")
231 {
233 }
234 else if (action == "HISTORYBACK")
235 {
237 }
238 else if (action == "HISTORYFORWARD")
239 {
241 }
242 else
243 {
244 handled = false;
245 }
246 }
247
248 return handled;
249}
250
251void MythWebEngineView::openBusyPopup(const QString &message)
252{
253 if (m_busyPopup)
254 return;
255
256 QString msg(tr("Downloading..."));
257
258 if (!message.isEmpty())
259 msg = message;
260
261 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
262 m_busyPopup = new MythUIBusyDialog(msg, popupStack, "downloadbusydialog");
263
264 if (m_busyPopup->Create())
265 popupStack->AddScreen(m_busyPopup, false);
266}
267
269{
270 if (m_busyPopup)
272
273 m_busyPopup = nullptr;
274}
275
277{
278 if (event->type() == DialogCompletionEvent::kEventType)
279 {
280 auto *dce = (DialogCompletionEvent *)(event);
281
282 // make sure the user didn't ESCAPE out of the dialog
283 if (dce->GetResult() < 0)
284 return;
285
286 QString resultid = dce->GetId();
287 QString resulttext = dce->GetResultText();
288
289 if (resultid == "filenamedialog")
290 {
291 //doDownload(resulttext);
292 }
293 else if (resultid == "downloadmenu")
294 {
295 if (resulttext == tr("Play the file"))
296 {
297 QFileInfo fi(m_downloadRequest.url().path());
298 QString extension = fi.suffix();
299 QString mimeType = getReplyMimetype();
300
301 if (isMusicFile(extension, mimeType))
302 {
303 MythEvent me(QString("MUSIC_COMMAND %1 PLAY_URL %2")
305 m_downloadRequest.url().toString()));
307 }
308 else if (isVideoFile(extension, mimeType))
309 {
310 GetMythMainWindow()->HandleMedia("Internal",
311 m_downloadRequest.url().toString());
312 }
313 else
314 {
315 LOG(VB_GENERAL, LOG_ERR,
316 QString("MythWebEngineView: Asked to play a file with "
317 "extension '%1' but don't know how")
318 .arg(extension));
319 }
320 }
321 else if (resulttext == tr("Download the file"))
322 {
323 //doDownloadRequested(m_downloadRequest);
324 }
325 else if (resulttext == tr("Download and play the file"))
326 {
327 m_downloadAndPlay = true;
328 //doDownloadRequested(m_downloadRequest);
329 }
330 }
331 }
332 else if (event->type() == MythEvent::kMythEventMessage)
333 {
334 auto *me = dynamic_cast<MythEvent *>(event);
335 if (me == nullptr)
336 return;
337
338 QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
339 if (tokens.isEmpty())
340 return;
341
342 if (tokens[0] == "DOWNLOAD_FILE")
343 {
344 QStringList args = me->ExtraDataList();
345
346 if (tokens[1] == "UPDATE")
347 {
348 // could update a progressbar here
349 }
350 else if (tokens[1] == "FINISHED")
351 {
352 int fileSize = args[2].toInt();
353 int errorCode = args[4].toInt();
354 const QString& filename = args[1];
355
357
358 if ((errorCode != 0) || (fileSize == 0))
359 ShowOkPopup(tr("ERROR downloading file."));
360 else if (m_downloadAndPlay)
361 GetMythMainWindow()->HandleMedia("Internal", filename);
362
363 MythEvent me2(QString("BROWSER_DOWNLOAD_FINISHED"), args);
365 }
366 }
367 }
368
369 if (event->type() == QEvent::UpdateRequest)
370 {
371 LOG(VB_GENERAL, LOG_ERR, QString("MythWebEngineView: customeEvent: QEvent::UpdateRequest"));
373 }
374}
375
377{
378 QFileInfo fi(m_downloadRequest.url().path());
379 QString extension = fi.suffix();
380 QString mimeType = getReplyMimetype();
381
382 QString label = tr("What do you want to do with this file?");
383
384 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
385
386 auto *menu = new MythDialogBox(label, popupStack, "downloadmenu");
387
388 if (!menu->Create())
389 {
390 delete menu;
391 return;
392 }
393
394 menu->SetReturnEvent(this, "downloadmenu");
395
396 if (isMusicFile(extension, mimeType))
397 menu->AddButton(tr("Play the file"));
398
399 if (isVideoFile(extension, mimeType))
400 menu->AddButton(tr("Download and play the file"));
401
402 menu->AddButton(tr("Download the file"));
403 menu->AddButton(tr("Cancel"));
404
405 popupStack->AddScreen(menu);
406}
407
408QString MythWebEngineView::getExtensionForMimetype(const QString &mimetype)
409{
410 if (mimetype.isEmpty())
411 return {""};
412
413 auto it = std::find_if(SupportedMimeTypes.cbegin(), SupportedMimeTypes.cend(),
414 [mimetype] (const MimeType& entry) -> bool
415 { return mimetype == entry.m_mimeType; });
416 if (it != SupportedMimeTypes.cend())
417 return it->m_extension;
418 return {""};
419}
420
421bool MythWebEngineView::isMusicFile(const QString &extension, const QString &mimetype)
422{
423 return std::any_of(SupportedMimeTypes.cbegin(), SupportedMimeTypes.cend(),
424 [extension, mimetype](const auto &entry){
425 if (entry.m_isVideo)
426 return false;
427 if (!mimetype.isEmpty() &&
428 mimetype == entry.m_mimeType)
429 return true;
430 if (!extension.isEmpty() &&
431 extension.toLower() == entry.m_extension)
432 return true;
433 return false; } );
434}
435
436bool MythWebEngineView::isVideoFile(const QString &extension, const QString &mimetype)
437{
438 return std::any_of(SupportedMimeTypes.cbegin(), SupportedMimeTypes.cend(),
439 [extension, mimetype](const auto &entry) {
440 if (!entry.m_isVideo)
441 return false;
442 if (!mimetype.isEmpty() &&
443 mimetype == entry.m_mimeType)
444 return true;
445 if (!extension.isEmpty() &&
446 extension.toLower() == entry.m_extension)
447 return true;
448 return false; } );
449}
450
452{
453 if (!m_downloadReply)
454 return {};
455
456 QString mimeType;
457 QVariant header = m_downloadReply->header(QNetworkRequest::ContentTypeHeader);
458
459 if (header != QVariant())
460 mimeType = header.toString();
461
462 return mimeType;
463}
464
465QWebEngineView *MythWebEngineView::createWindow(QWebEnginePage::WebWindowType /* type */)
466{
467 return (QWebEngineView *) this;
468}
469
470
511 : MythUIType(parent, name),
512 m_updateInterval(500), m_bgColor("Red"), m_userCssFile(""),
513 m_defaultSaveDir(GetConfDir() + "/MythBrowser/"),
514 m_defaultSaveFilename(""), m_lastMouseAction("")
515{
516 SetCanTakeFocus(true);
517 m_lastUpdateTime.start();
518}
519
524{
526
527 Init();
528}
529
538{
539 // only do the initialisation for widgets not being stored in the global object store
540 if (parent() == GetGlobalObjectStore())
541 return;
542
543 if (m_initialized)
544 return;
545
548 m_actualBrowserArea.translate(m_area.x(), m_area.y());
549
550 if (!m_actualBrowserArea.isValid())
552
553 m_webEngine = new MythWebEngineView(GetMythMainWindow()->GetPaintWindow(), this);
554 m_webEngine->setPalette(QApplication::style()->standardPalette());
555 m_webEngine->setGeometry(m_actualBrowserArea);
556 m_webEngine->setFixedSize(m_actualBrowserArea.size());
558 m_webEngine->show();
559 m_webEngine->raise();
560 m_webEngine->setFocus();
561 m_webEngine->update();
562
563 SetRedraw();
564
565 bool err = false;
566 UIUtilW::Assign(this, m_horizontalScrollbar, "horizscrollbar", &err);
567 UIUtilW::Assign(this, m_verticalScrollbar, "vertscrollbar", &err);
568
570 {
575 }
576
578 {
583 }
584
585 // if we have a valid css URL use that ...
586 if (!m_userCssFile.isEmpty())
587 {
588 QString filename = m_userCssFile;
589
590 if (GetMythUI()->FindThemeFile(filename))
591 LoadUserStyleSheet(QUrl("file://" + filename));
592 }
593 else
594 {
595 // ...otherwise use the default one
596 QString filename = "htmls/mythbrowser.css";
597
598 if (GetMythUI()->FindThemeFile(filename))
599 LoadUserStyleSheet(QUrl("file://" + filename));
600 }
601
602 SetHttpUserAgent("Mozilla/5.0 (SMART-TV; Linux; Tizen 5.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/5.0 NativeTVAds Safari/538.1");
603
604 connect(m_webEngine, &QWebEngineView::loadStarted, this, &MythUIWebBrowser::slotLoadStarted);
605 connect(m_webEngine, &QWebEngineView::loadFinished, this, &MythUIWebBrowser::slotLoadFinished);
606 connect(m_webEngine, &QWebEngineView::loadProgress, this, &MythUIWebBrowser::slotLoadProgress);
607 connect(m_webEngine, &QWebEngineView::titleChanged, this, &MythUIWebBrowser::slotTitleChanged);
608 connect(m_webEngine, &QWebEngineView::iconChanged, this, &MythUIWebBrowser::slotIconChanged);
609 connect(m_webEngine, &QWebEngineView::iconUrlChanged, this, &MythUIWebBrowser::slotIconUrlChanged);
610 connect(m_webEngine, &QWebEngineView::renderProcessTerminated, this, &MythUIWebBrowser::slotRenderProcessTerminated);
611
612 connect(m_webEngine->page(), &QWebEnginePage::contentsSizeChanged, this, &MythUIWebBrowser::slotContentsSizeChanged);
613 connect(m_webEngine->page(), &QWebEnginePage::scrollPositionChanged, this, &MythUIWebBrowser::slotScrollPositionChanged);
614 connect(m_webEngine->page(), &QWebEnginePage::linkHovered, this, &MythUIWebBrowser::slotStatusBarMessage);
615 connect(m_webEngine->page(), &QWebEnginePage::fullScreenRequested, this, &MythUIWebBrowser::slotFullScreenRequested);
616 connect(m_webEngine->page(), &QWebEnginePage::windowCloseRequested, this, &MythUIWebBrowser::slotWindowCloseRequested);
617
620
621 // find what screen we are on
622 m_parentScreen = nullptr;
623 QObject *parentObject = parent();
624
625 while (parentObject)
626 {
627 m_parentScreen = qobject_cast<MythScreenType *>(parentObject);
628
629 if (m_parentScreen)
630 break;
631
632 parentObject = parentObject->parent();
633 }
634
635 if (!m_parentScreen && parent() != GetGlobalObjectStore())
636 LOG(VB_GENERAL, LOG_ERR, "MythUIWebBrowser: failed to find our parent screen");
637
638 // connect to the topScreenChanged signals on each screen stack
639 for (int x = 0; x < GetMythMainWindow()->GetStackCount(); x++)
640 {
642
643 if (stack)
645 }
646
647 if (gCoreContext->GetNumSetting("WebBrowserEnablePlugins", 1) == 1)
648 {
649 LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: enabling plugins");
650 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
651 }
652 else
653 {
654 LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling plugins");
655 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, false);
656 }
657
658 if (!gCoreContext->GetBoolSetting("WebBrowserEnableJavascript", true))
659 {
660 LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling JavaScript");
661 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
662 }
663
664 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
665 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, true);
666 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
667 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
668 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, true);
669 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
670
672 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::ShowScrollBars, false);
673
674 m_webEngine->setWindowFlag(Qt::WindowStaysOnTopHint, true);
675
676 QImage image = QImage(m_actualBrowserArea.size(), QImage::Format_ARGB32);
678 m_image->Assign(image);
679
681
682 m_zoom = gCoreContext->GetFloatSetting("WebBrowserZoomLevel", 1.0);
683
685
686 if (!m_widgetUrl.isEmpty() && m_widgetUrl.isValid())
688
689 m_initialized = true;
690}
691
696{
697 if (m_webEngine)
698 {
699 m_webEngine->hide();
700 m_webEngine->disconnect();
701 m_webEngine->deleteLater();
702 m_webEngine = nullptr;
703 }
704
705 if (m_image)
706 {
707 m_image->DecrRef();
708 m_image = nullptr;
709 }
710}
711
716void MythUIWebBrowser::LoadPage(const QUrl& url)
717{
718 if (!m_webEngine)
719 return;
720
722
723 m_webEngine->setUrl(url);
724}
725
732void MythUIWebBrowser::SetHtml(const QString &html, const QUrl &baseUrl)
733{
734 if (!m_webEngine)
735 return;
736
738
739 m_webEngine->setHtml(html, baseUrl);
740}
741
746void MythUIWebBrowser::LoadUserStyleSheet(const QUrl& url, const QString &name)
747{
748 if (!m_webEngine)
749 return;
750
751 // download the style sheet
752 QByteArray download;
753 if (!GetMythDownloadManager()->download(url.toString(), &download))
754 {
755 LOG(VB_GENERAL, LOG_ERR,"MythUIWebBrowser: Failed to download css from - " + url.toString());
756 return;
757 }
758
759 RemoveUserStyleSheet("mythtv");
760
761 LOG(VB_GENERAL, LOG_INFO,"MythUIWebBrowser: Loading css from - " + url.toString());
762
763 QWebEngineScript script;
764 QString s = QString::fromLatin1("(function() {"\
765 " css = document.createElement('style');"\
766 " css.type = 'text/css';"\
767 " css.id = '%1';"\
768 " document.head.appendChild(css);"\
769 " css.innerText = '%2';"\
770 "})()").arg(name, QString(download).simplified());
771
772 m_webEngine->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
773
774 script.setName(name);
775 script.setSourceCode(s);
776 script.setInjectionPoint(QWebEngineScript::DocumentReady);
777 script.setRunsOnSubFrames(true);
778 script.setWorldId(QWebEngineScript::ApplicationWorld);
779 m_webEngine->page()->scripts().insert(script);
780}
781
783{
784 if (!m_webEngine)
785 return;
786
787#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
788 QList<QWebEngineScript> scripts = m_webEngine->page()->scripts().find(name);
789#else
790 QList<QWebEngineScript> scripts = m_webEngine->page()->scripts().findScripts(name);
791#endif
792
793 if (!scripts.isEmpty())
794 {
795 m_webEngine->page()->scripts().remove(scripts[0]);
796
797 QString s = QString::fromLatin1("(function() {"\
798 " var element = document.getElementById('%1');"\
799 " element.outerHTML = '';"\
800 " delete element;"\
801 "})()").arg(name);
802
803 m_webEngine->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
804 }
805}
806
808{
809 if (!m_webEngine)
810 return nullptr;
811
812 return m_webEngine->settings();
813}
814
816{
817 if (!m_webEngine)
818 return nullptr;
819
820 return m_webEngine->page()->profile();
821}
822
823void MythUIWebBrowser::SetHttpUserAgent(const QString &userAgent)
824{
825 if (!m_webEngine)
826 return;
827
828 m_webEngine->page()->profile()->setHttpUserAgent(userAgent);
829}
830
838{
839 if (!m_webEngine)
840 return;
841
842 color.setAlpha(255);
843 QPalette palette = m_webEngine->palette();
844 palette.setBrush(QPalette::Window, QBrush(color));
845 palette.setBrush(QPalette::Base, QBrush(color));
846 m_webEngine->setPalette(palette);
847
848 UpdateBuffer();
849}
850
862{
863 if (!m_webEngine)
864 return;
865
866 if (m_active == active)
867 return;
868
869 m_active = active;
870 m_wasActive = active;
871
872 if (m_active)
873 {
874 m_webEngine->setUpdatesEnabled(false);
875 m_webEngine->setParent(GetMythMainWindow()->GetPaintWindow());
876
877 if (m_hasFocus)
878 m_webEngine->setFocus();
879
880 m_webEngine->show();
881 m_webEngine->raise();
882 m_webEngine->setUpdatesEnabled(true);
883 }
884 else
885 {
886 UpdateBuffer();
887
888 m_webEngine->setParent(GetMythMainWindow());
889 m_webEngine->clearFocus();
890 }
891}
892
897{
898 SetZoom(m_zoom + 0.1);
899}
900
905{
906 SetZoom(m_zoom - 0.1);
907}
908
914{
915 if (!m_webEngine)
916 return;
917
918 m_zoom = std::clamp(zoom, 0.3, 5.0);
919 m_webEngine->setZoomFactor(m_zoom);
920
922 UpdateBuffer();
923
924 slotStatusBarMessage(tr("Zoom: %1%").arg(m_zoom * 100));
925
926 gCoreContext->SaveSetting("WebBrowserZoomLevel", QString("%1").arg(m_zoom));
927}
928
929void MythUIWebBrowser::Reload(bool useCache)
930{
931 if (useCache)
932 TriggerPageAction(QWebEnginePage::Reload);
933 else
934 TriggerPageAction(QWebEnginePage::QWebEnginePage::ReloadAndBypassCache);
935}
936
937void MythUIWebBrowser::TriggerPageAction(QWebEnginePage::WebAction action, bool checked)
938{
939 if (!m_webEngine)
940 return;
941
942 m_webEngine->triggerPageAction(action, checked);
943}
944
946{
947 if (!saveDir.isEmpty())
948 m_defaultSaveDir = saveDir;
949 else
950 m_defaultSaveDir = GetConfDir() + "/MythBrowser/";
951}
952
954{
955 if (!filename.isEmpty())
957 else
958 m_defaultSaveFilename.clear();
959}
960
966{
967 return m_zoom;
968}
969
976{
977 if (!m_webEngine)
978 return false;
979
980 return m_webEngine->history()->canGoForward();
981}
982
989{
990 if (!m_webEngine)
991 return false;
992
993 return m_webEngine->history()->canGoBack();
994}
995
1000{
1001 if (!m_webEngine)
1002 return;
1003
1004 m_webEngine->back();
1005}
1006
1011{
1012 if (!m_webEngine)
1013 return;
1014
1015 m_webEngine->forward();
1016}
1017
1023{
1024 if (m_webEngine)
1025 return m_webEngine->icon();
1026
1027 return {};
1028}
1029
1035{
1036 if (m_webEngine)
1037 return m_webEngine->url();
1038
1039 return {};
1040}
1041
1047{
1048 if (m_webEngine)
1049 return m_webEngine->title();
1050
1051 return {""};
1052}
1053
1058void MythUIWebBrowser::RunJavaScript(const QString &scriptSource)
1059{
1060 if (m_webEngine)
1061 m_webEngine->page()->runJavaScript(scriptSource);
1062}
1063
1065{
1067}
1068
1070{
1071 SetActive(true);
1072 m_webEngine->setFocus();
1073}
1074
1076{
1077 GetMythMainWindow()->GetPaintWindow()->setFocus();
1078 m_webEngine->clearFocus();
1079}
1080
1081void MythUIWebBrowser::slotRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode)
1082{
1083 LOG(VB_GENERAL, LOG_INFO, QString("MythUIWebBrowser::slotRenderProcessTerminated - terminationStatus: %1, exitStatus: %2").arg(terminationStatus).arg(exitCode));
1084 m_webEngine->reload();
1085}
1086
1087void MythUIWebBrowser::slotFullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest)
1088{
1089 fullScreenRequest.accept();
1090}
1091
1093{
1094 LOG(VB_GENERAL, LOG_INFO, QString("MythUIWebBrowser::slotWindowCloseRequested called"));
1095
1096 Back();
1097}
1098
1099void MythUIWebBrowser::MythUIWebBrowser::slotLoadStarted(void)
1100{
1101 ResetScrollBars();
1102
1103 emit loadStarted();
1104}
1105
1107{
1108 UpdateBuffer();
1109 emit loadFinished(ok);
1110}
1111
1113{
1114 emit loadProgress(progress);
1115}
1116
1117void MythUIWebBrowser::slotTitleChanged(const QString &title)
1118{
1119 emit titleChanged(title);
1120}
1121
1123{
1124 emit statusBarMessage(text);
1125}
1126
1128{
1129 LoadPage(url);
1130}
1131
1133{
1134 emit iconChanged(icon);
1135}
1136
1138{
1139 emit iconUrlChanged(url);
1140}
1141
1142void MythUIWebBrowser::slotScrollPositionChanged(const QPointF /*position*/)
1143{
1145}
1146
1148{
1150}
1151
1153{
1154// bool wasActive = (m_wasActive || m_active);
1155// SetActive(false);
1156// m_wasActive = wasActive;
1157}
1158
1160{
1161// SetActive(m_wasActive);
1162// slotTopScreenChanged(nullptr);
1163}
1164
1166{
1167 UpdateBuffer();
1168
1169 if (IsOnTopScreen())
1171 else
1172 {
1173 bool wasActive = (m_wasActive || m_active);
1174 SetActive(false);
1175 m_wasActive = wasActive;
1176 }
1177}
1178
1181{
1182 if (!m_parentScreen)
1183 return false;
1184
1185 for (int x = GetMythMainWindow()->GetStackCount() - 1; x >= 0; x--)
1186 {
1188
1189 // ignore stacks with no screens on them
1190 if (!stack->GetTopScreen())
1191 continue;
1192
1193 return (stack->GetTopScreen() == m_parentScreen);
1194 }
1195
1196 return false;
1197}
1198
1199
1201{
1202 if (!m_webEngine)
1203 return;
1204
1205 QPoint position = m_webEngine->page()->scrollPosition().toPoint();
1207 {
1208 int maximum = m_webEngine->page()->contentsSize().height() - m_actualBrowserArea.height();
1212 }
1213
1215 {
1216 int maximum = m_webEngine->page()->contentsSize().width() - m_actualBrowserArea.width();
1220 }
1221}
1222
1224{
1226
1227 if (!m_image || !m_webEngine)
1228 {
1229 LOG(VB_GENERAL, LOG_ERR, QString("MythUIWebBrowser::UpdateBuffer called - m_image or m_webEngine is FALSE"));
1230 return;
1231 }
1232
1233 if (!m_active || (m_active && m_webEngine->hasFocus()))
1234 {
1235 m_webEngine->render(m_image);
1237 Refresh();
1238 }
1239}
1240
1245{
1246 //SetRedraw();
1247
1248 if (!m_webEngine)
1249 return;
1250
1252 {
1253 UpdateBuffer();
1254 m_lastUpdateTime.start();
1255 }
1256
1258}
1259
1263void MythUIWebBrowser::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1264 int alphaMod, QRect clipRect)
1265{
1266 if (!m_image || m_image->isNull() || !m_webEngine || m_webEngine->hasFocus())
1267 return;
1268
1269 QRect area = m_actualBrowserArea;
1270 area.translate(xoffset, yoffset);
1271
1272 p->SetClipRect(clipRect);
1273 p->DrawImage(area.x(), area.y(), m_image, alphaMod);
1274}
1275
1277{
1278 int step = 5;
1279
1280 // speed up mouse movement if the same key is held down
1281 if (action == m_lastMouseAction &&
1282 m_lastMouseActionTime.isValid() &&
1283 !m_lastMouseActionTime.hasExpired(500))
1284 {
1285 m_lastMouseActionTime.start();
1287
1288 if (m_mouseKeyCount > 5)
1289 step = 25;
1290 }
1291 else
1292 {
1294 m_lastMouseActionTime.start();
1295 m_mouseKeyCount = 1;
1296 }
1297
1298 if (action == "MOUSEUP")
1299 {
1300 QPoint curPos = QCursor::pos();
1301 QCursor::setPos(curPos.x(), curPos.y() - step);
1302 }
1303 else if (action == "MOUSELEFT")
1304 {
1305 QPoint curPos = QCursor::pos();
1306 QCursor::setPos(curPos.x() - step, curPos.y());
1307 }
1308 else if (action == "MOUSERIGHT")
1309 {
1310 QPoint curPos = QCursor::pos();
1311 QCursor::setPos(curPos.x() + step, curPos.y());
1312 }
1313 else if (action == "MOUSEDOWN")
1314 {
1315 QPoint curPos = QCursor::pos();
1316 QCursor::setPos(curPos.x(), curPos.y() + step);
1317 }
1318 else if (action == "MOUSELEFTBUTTON")
1319 {
1320 QPoint curPos = QCursor::pos();
1321 QWidget *widget = QApplication::widgetAt(curPos);
1322
1323 if (widget)
1324 {
1325 curPos = widget->mapFromGlobal(curPos);
1326
1327 auto *me = new QMouseEvent(QEvent::MouseButtonPress, curPos, curPos,
1328 Qt::LeftButton, Qt::LeftButton,
1329 Qt::NoModifier);
1330 //FIXME
1331 QCoreApplication::postEvent(widget, me);
1332
1333 me = new QMouseEvent(QEvent::MouseButtonRelease, curPos, curPos,
1334 Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1335 //FIXME
1336 QCoreApplication::postEvent(widget, me);
1337 }
1338 }
1339}
1340
1342{
1344 {
1347 }
1348
1350 {
1353 }
1354}
1355
1360 const QString &filename, QDomElement &element, bool showWarnings)
1361{
1362 if (element.tagName() == "zoom")
1363 {
1364 QString zoom = getFirstText(element);
1365 m_zoom = zoom.toDouble();
1366 }
1367 else if (element.tagName() == "url")
1368 {
1369 m_widgetUrl.setUrl(getFirstText(element));
1370 }
1371 else if (element.tagName() == "userstylesheet")
1372 {
1373 m_userCssFile = getFirstText(element);
1374 }
1375 else if (element.tagName() == "updateinterval")
1376 {
1377 QString interval = getFirstText(element);
1378 m_updateInterval = interval.toInt();
1379 }
1380 else if (element.tagName() == "background")
1381 {
1382 m_bgColor = QColor(element.attribute("color", "#ffffff"));
1383 int alpha = element.attribute("alpha", "255").toInt();
1384 m_bgColor.setAlpha(alpha);
1385 }
1386 else if (element.tagName() == "browserarea")
1387 {
1388 m_browserArea = parseRect(element);
1389 }
1390 else if (element.tagName() == "scrollduration")
1391 {
1392 // this is no longer used QWebEngine has its own scroll animation
1393 }
1394 else if (element.tagName() == "acceptsfocus")
1395 {
1396 SetCanTakeFocus(parseBool(element));
1397 }
1398 else
1399 {
1400 return MythUIType::ParseElement(filename, element, showWarnings);
1401 }
1402
1403 return true;
1404}
1405
1410{
1411 auto *browser = dynamic_cast<MythUIWebBrowser *>(base);
1412 if (!browser)
1413 {
1414 LOG(VB_GENERAL, LOG_ERR, "ERROR, bad parsing");
1415 return;
1416 }
1417
1419
1420 m_browserArea = browser->m_browserArea;
1421 m_zoom = browser->m_zoom;
1422 m_bgColor = browser->m_bgColor;
1423 m_widgetUrl = browser->m_widgetUrl;
1424 m_userCssFile = browser->m_userCssFile;
1425 m_updateInterval = browser->m_updateInterval;
1426 m_defaultSaveDir = browser->m_defaultSaveDir;
1427 m_defaultSaveFilename = browser->m_defaultSaveFilename;
1428 //m_scrollAnimation.setDuration(browser->m_scrollAnimation.duration());
1429}
1430
1435{
1436 auto *browser = new MythUIWebBrowser(parent, objectName());
1437 browser->CopyFrom(this);
1438}
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
static const Type kEventType
Definition: mythdialogbox.h:56
QString GetHostName(void)
void SaveSetting(const QString &key, int newValue)
double GetFloatSetting(const QString &key, double defaultval=0.0)
void dispatch(const MythEvent &event)
int GetNumSetting(const QString &key, int defaultval=0)
bool GetBoolSetting(const QString &key, bool defaultval=false)
Basic menu dialog, message and a list of options.
This class is used as a container for messages.
Definition: mythevent.h:17
const QString & Message() const
Definition: mythevent.h:65
static const Type kMythEventMessage
Definition: mythevent.h:79
virtual void SetChanged(bool change=true)
Definition: mythimage.h:50
void Assign(const QImage &img)
Definition: mythimage.cpp:77
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:52
QWidget * GetPaintWindow()
MythScreenStack * GetStackAt(int Position)
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
bool HandleMedia(const QString &Handler, const QString &Mrl, const QString &Plot="", const QString &Title="", const QString &Subtitle="", const QString &Director="", int Season=0, int Episode=0, const QString &Inetref="", std::chrono::minutes LenMins=2h, const QString &Year="1895", const QString &Id="", bool UseBookmarks=false)
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
void CalculateArea(QRect parentArea)
Definition: mythrect.cpp:64
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void topScreenChanged(MythScreenType *screen)
virtual MythScreenType * GetTopScreen(void) const
Screen in which all other widgets are contained and rendered.
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
virtual void Close()
bool Create(void) override
bool Create(void) override
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
void SetMaximum(int value)
void SetPageStep(int value)
void SetSliderPosition(int value)
The base class on which all widgets and screens are based.
Definition: mythuitype.h:86
void SetCanTakeFocus(bool set=true)
Set whether this widget can take focus.
Definition: mythuitype.cpp:362
void TakingFocus(void)
void Hiding(void)
virtual MythPainter * GetPainter(void)
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
void SetRedraw(void)
Definition: mythuitype.cpp:313
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:456
void Hide(void)
void Refresh(void)
bool m_hasFocus
Definition: mythuitype.h:264
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
void Showing(void)
MythRect m_area
Definition: mythuitype.h:277
void LosingFocus(void)
Web browsing widget.
bool IsOnTopScreen(void)
is our containing screen the top screen?
MythRect m_actualBrowserArea
void iconUrlChanged(const QUrl &url)
a pages fav icon has changed
void slotScrollBarHiding(void)
~MythUIWebBrowser() override
the classes destructor
void slotLinkClicked(const QUrl &url)
QWebEngineView * m_webEngine
QString GetTitle(void)
Gets the current page's title.
void iconChanged(const QIcon &icon)
link hit test messages
MythScreenType * GetParentScreen(void)
float GetZoom(void) const
Get the current zoom level.
bool IsInputToggled(void) const
returns true if all keypresses are to be passed to the web page
void titleChanged(const QString &title)
% of page loaded
void SetDefaultSaveDirectory(const QString &saveDir)
void LoadPage(const QUrl &url)
Loads the specified url and displays it.
void SetZoom(double zoom)
Set the text size to specific size.
void HandleMouseAction(const QString &action)
void RemoveUserStyleSheet(const QString &name)
QElapsedTimer m_lastUpdateTime
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
QIcon GetIcon(void)
Gets the current page's fav icon.
void Init(void)
Initializes the widget ready for use.
void slotLoadFinished(bool Ok)
void RunJavaScript(const QString &scriptSource)
void slotWindowCloseRequested(void)
void TriggerPageAction(QWebEnginePage::WebAction action, bool checked=false)
void SetInputToggled(bool inputToggled)
void SetActive(bool active)
Toggles the active state of the widget.
MythUIScrollBar * m_horizontalScrollbar
void ZoomIn(void)
Increase the text size.
void slotTopScreenChanged(MythScreenType *screen)
void slotScrollBarShowing(void)
void Reload(bool useCache=true)
void Back(void)
Got backward in page history.
MythUIWebBrowser(MythUIType *parent, const QString &name)
the classes constructor
void statusBarMessage(const QString &text)
a pages title has changed
void slotIconChanged(const QIcon &icon)
void SetHttpUserAgent(const QString &userAgent)
void loadProgress(int progress)
a page has finished loading
QString m_defaultSaveFilename
void slotTitleChanged(const QString &title)
void slotScrollPositionChanged(QPointF position)
MythScreenType * m_parentScreen
QWebEngineSettings * GetWebEngineSettings(void)
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
void SetDefaultSaveFilename(const QString &filename)
QWebEngineProfile * GetWebEngineProfile(void)
void SendStatusBarMessage(const QString &text)
void slotLoadStarted(void)
a file has been downloaded
void ZoomOut(void)
Decrease the text size.
QUrl GetUrl(void)
Gets the current page's url.
QElapsedTimer m_lastMouseActionTime
void LoadUserStyleSheet(const QUrl &url, const QString &name=QString("mythtv"))
Sets the specified user style sheet.
void SetBackgroundColor(QColor color)
Sets the default background color.
bool CanGoBack(void)
Can we go backward in page history.
bool CanGoForward(void)
Can go forward in page history.
void SetHtml(const QString &html, const QUrl &baseUrl=QUrl())
Sets the content of the widget to the specified html.
void Forward(void)
Got forward in page history.
void slotRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode)
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
MythUIScrollBar * m_verticalScrollbar
void loadFinished(bool ok)
a page is starting to load
static void slotFullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest)
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
void slotContentsSizeChanged(QSizeF size)
void slotIconUrlChanged(const QUrl &url)
void slotLoadProgress(int progress)
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
void slotStatusBarMessage(const QString &text)
Subclass of QWebEngineView.
void sendKeyPress(int key, Qt::KeyboardModifiers modifiers, const QString &text=QString())
QString getReplyMimetype(void)
MythWebEngineView(QWidget *parent, MythUIWebBrowser *parentBrowser)
QWebEngineView * createWindow(QWebEnginePage::WebWindowType type) override
QWebEngineProfile * m_profile
QNetworkRequest m_downloadRequest
MythUIWebBrowser * m_parentBrowser
static bool isMusicFile(const QString &extension, const QString &mimetype)
QWebEnginePage * m_webpage
static bool isVideoFile(const QString &extension, const QString &mimetype)
void openBusyPopup(const QString &message)
~MythWebEngineView(void) override
void customEvent(QEvent *e) override
bool eventFilter(QObject *obj, QEvent *event) override
static QString getExtensionForMimetype(const QString &mimetype)
bool handleKeyPress(QKeyEvent *event)
QNetworkReply * m_downloadReply
MythUIBusyDialog * m_busyPopup
static MythUIType * GetGlobalObjectStore(void)
static MythRect parseRect(const QString &text, bool normalize=true)
static QString getFirstText(QDomElement &element)
static bool parseBool(const QString &text)
static guint32 * back
Definition: goom_core.cpp:25
bool progress
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString GetConfDir(void)
Definition: mythdirs.cpp:263
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
static MythThemedMenu * menu
MythUIHelper * GetMythUI()
static const std::vector< MimeType > SupportedMimeTypes
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
QString m_mimeType
QString m_extension
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27