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 for (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::ranges::find(SupportedMimeTypes, mimetype,
415 if (it != SupportedMimeTypes.end())
416 return it->m_extension;
417 return {""};
418}
419
420bool MythWebEngineView::isMusicFile(const QString &extension, const QString &mimetype)
421{
422 return std::ranges::any_of(SupportedMimeTypes,
423 [extension, mimetype](const auto &entry){
424 if (entry.m_isVideo)
425 return false;
426 if (!mimetype.isEmpty() &&
427 mimetype == entry.m_mimeType)
428 return true;
429 if (!extension.isEmpty() &&
430 extension.toLower() == entry.m_extension)
431 return true;
432 return false; } );
433}
434
435bool MythWebEngineView::isVideoFile(const QString &extension, const QString &mimetype)
436{
437 return std::ranges::any_of(SupportedMimeTypes,
438 [extension, mimetype](const auto &entry) {
439 if (!entry.m_isVideo)
440 return false;
441 if (!mimetype.isEmpty() &&
442 mimetype == entry.m_mimeType)
443 return true;
444 if (!extension.isEmpty() &&
445 extension.toLower() == entry.m_extension)
446 return true;
447 return false; } );
448}
449
451{
452 if (!m_downloadReply)
453 return {};
454
455 QString mimeType;
456 QVariant header = m_downloadReply->header(QNetworkRequest::ContentTypeHeader);
457
458 if (header != QVariant())
459 mimeType = header.toString();
460
461 return mimeType;
462}
463
464QWebEngineView *MythWebEngineView::createWindow(QWebEnginePage::WebWindowType /* type */)
465{
466 return (QWebEngineView *) this;
467}
468
469
510 : MythUIType(parent, name),
511 m_updateInterval(500), m_bgColor("Red"), m_userCssFile(""),
512 m_defaultSaveDir(GetConfDir() + "/MythBrowser/"),
513 m_defaultSaveFilename(""), m_lastMouseAction("")
514{
515 SetCanTakeFocus(true);
516 m_lastUpdateTime.start();
517}
518
523{
525
526 Init();
527}
528
537{
538 // only do the initialisation for widgets not being stored in the global object store
539 if (parent() == GetGlobalObjectStore())
540 return;
541
542 if (m_initialized)
543 return;
544
547 m_actualBrowserArea.translate(m_area.x(), m_area.y());
548
549 if (!m_actualBrowserArea.isValid())
551
552 m_webEngine = new MythWebEngineView(GetMythMainWindow()->GetPaintWindow(), this);
553 m_webEngine->setPalette(QApplication::style()->standardPalette());
554 m_webEngine->setGeometry(m_actualBrowserArea);
555 m_webEngine->setFixedSize(m_actualBrowserArea.size());
557 m_webEngine->show();
558 m_webEngine->raise();
559 m_webEngine->setFocus();
560 m_webEngine->update();
561
562 SetRedraw();
563
564 bool err = false;
565 UIUtilW::Assign(this, m_horizontalScrollbar, "horizscrollbar", &err);
566 UIUtilW::Assign(this, m_verticalScrollbar, "vertscrollbar", &err);
567
569 {
574 }
575
577 {
582 }
583
584 // if we have a valid css URL use that ...
585 if (!m_userCssFile.isEmpty())
586 {
587 QString filename = m_userCssFile;
588
589 if (GetMythUI()->FindThemeFile(filename))
590 LoadUserStyleSheet(QUrl("file://" + filename));
591 }
592 else
593 {
594 // ...otherwise use the default one
595 QString filename = "htmls/mythbrowser.css";
596
597 if (GetMythUI()->FindThemeFile(filename))
598 LoadUserStyleSheet(QUrl("file://" + filename));
599 }
600
601 SetHttpUserAgent("Mozilla/5.0 (SMART-TV; Linux; Tizen 5.0) AppleWebKit/538.1 (KHTML, like Gecko) Version/5.0 NativeTVAds Safari/538.1");
602
603 connect(m_webEngine, &QWebEngineView::loadStarted, this, &MythUIWebBrowser::slotLoadStarted);
604 connect(m_webEngine, &QWebEngineView::loadFinished, this, &MythUIWebBrowser::slotLoadFinished);
605 connect(m_webEngine, &QWebEngineView::loadProgress, this, &MythUIWebBrowser::slotLoadProgress);
606 connect(m_webEngine, &QWebEngineView::titleChanged, this, &MythUIWebBrowser::slotTitleChanged);
607 connect(m_webEngine, &QWebEngineView::iconChanged, this, &MythUIWebBrowser::slotIconChanged);
608 connect(m_webEngine, &QWebEngineView::iconUrlChanged, this, &MythUIWebBrowser::slotIconUrlChanged);
609 connect(m_webEngine, &QWebEngineView::renderProcessTerminated, this, &MythUIWebBrowser::slotRenderProcessTerminated);
610
611 connect(m_webEngine->page(), &QWebEnginePage::contentsSizeChanged, this, &MythUIWebBrowser::slotContentsSizeChanged);
612 connect(m_webEngine->page(), &QWebEnginePage::scrollPositionChanged, this, &MythUIWebBrowser::slotScrollPositionChanged);
613 connect(m_webEngine->page(), &QWebEnginePage::linkHovered, this, &MythUIWebBrowser::slotStatusBarMessage);
614 connect(m_webEngine->page(), &QWebEnginePage::fullScreenRequested, this, &MythUIWebBrowser::slotFullScreenRequested);
615 connect(m_webEngine->page(), &QWebEnginePage::windowCloseRequested, this, &MythUIWebBrowser::slotWindowCloseRequested);
616
619
620 // find what screen we are on
621 m_parentScreen = nullptr;
622 QObject *parentObject = parent();
623
624 while (parentObject)
625 {
626 m_parentScreen = qobject_cast<MythScreenType *>(parentObject);
627
628 if (m_parentScreen)
629 break;
630
631 parentObject = parentObject->parent();
632 }
633
634 if (!m_parentScreen && parent() != GetGlobalObjectStore())
635 LOG(VB_GENERAL, LOG_ERR, "MythUIWebBrowser: failed to find our parent screen");
636
637 // connect to the topScreenChanged signals on each screen stack
638 for (int x = 0; x < GetMythMainWindow()->GetStackCount(); x++)
639 {
641
642 if (stack)
644 }
645
646 if (gCoreContext->GetNumSetting("WebBrowserEnablePlugins", 1) == 1)
647 {
648 LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: enabling plugins");
649 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
650 }
651 else
652 {
653 LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling plugins");
654 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, false);
655 }
656
657 if (!gCoreContext->GetBoolSetting("WebBrowserEnableJavascript", true))
658 {
659 LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling JavaScript");
660 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
661 }
662
663 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
664 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, true);
665 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
666 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, true);
667 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, true);
668 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
669
671 m_webEngine->page()->settings()->setAttribute(QWebEngineSettings::ShowScrollBars, false);
672
673 m_webEngine->setWindowFlag(Qt::WindowStaysOnTopHint, true);
674
675 QImage image = QImage(m_actualBrowserArea.size(), QImage::Format_ARGB32);
677 m_image->Assign(image);
678
680
681 m_zoom = gCoreContext->GetFloatSetting("WebBrowserZoomLevel", 1.0);
682
684
685 if (!m_widgetUrl.isEmpty() && m_widgetUrl.isValid())
687
688 m_initialized = true;
689}
690
695{
696 if (m_webEngine)
697 {
698 m_webEngine->hide();
699 m_webEngine->disconnect();
700 m_webEngine->deleteLater();
701 m_webEngine = nullptr;
702 }
703
704 if (m_image)
705 {
706 m_image->DecrRef();
707 m_image = nullptr;
708 }
709}
710
715void MythUIWebBrowser::LoadPage(const QUrl& url)
716{
717 if (!m_webEngine)
718 return;
719
721
722 m_webEngine->setUrl(url);
723}
724
731void MythUIWebBrowser::SetHtml(const QString &html, const QUrl &baseUrl)
732{
733 if (!m_webEngine)
734 return;
735
737
738 m_webEngine->setHtml(html, baseUrl);
739}
740
745void MythUIWebBrowser::LoadUserStyleSheet(const QUrl& url, const QString &name)
746{
747 if (!m_webEngine)
748 return;
749
750 // download the style sheet
751 QByteArray download;
752 if (!GetMythDownloadManager()->download(url.toString(), &download))
753 {
754 LOG(VB_GENERAL, LOG_ERR,"MythUIWebBrowser: Failed to download css from - " + url.toString());
755 return;
756 }
757
758 RemoveUserStyleSheet("mythtv");
759
760 LOG(VB_GENERAL, LOG_INFO,"MythUIWebBrowser: Loading css from - " + url.toString());
761
762 QWebEngineScript script;
763 QString s = QString::fromLatin1("(function() {"\
764 " css = document.createElement('style');"\
765 " css.type = 'text/css';"\
766 " css.id = '%1';"\
767 " document.head.appendChild(css);"\
768 " css.innerText = '%2';"\
769 "})()").arg(name, QString(download).simplified());
770
771 m_webEngine->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
772
773 script.setName(name);
774 script.setSourceCode(s);
775 script.setInjectionPoint(QWebEngineScript::DocumentReady);
776 script.setRunsOnSubFrames(true);
777 script.setWorldId(QWebEngineScript::ApplicationWorld);
778 m_webEngine->page()->scripts().insert(script);
779}
780
782{
783 if (!m_webEngine)
784 return;
785
786#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
787 QList<QWebEngineScript> scripts = m_webEngine->page()->scripts().find(name);
788#else
789 QList<QWebEngineScript> scripts = m_webEngine->page()->scripts().findScripts(name);
790#endif
791
792 if (!scripts.isEmpty())
793 {
794 m_webEngine->page()->scripts().remove(scripts[0]);
795
796 QString s = QString::fromLatin1("(function() {"\
797 " var element = document.getElementById('%1');"\
798 " element.outerHTML = '';"\
799 " delete element;"\
800 "})()").arg(name);
801
802 m_webEngine->page()->runJavaScript(s, QWebEngineScript::ApplicationWorld);
803 }
804}
805
807{
808 if (!m_webEngine)
809 return nullptr;
810
811 return m_webEngine->settings();
812}
813
815{
816 if (!m_webEngine)
817 return nullptr;
818
819 return m_webEngine->page()->profile();
820}
821
822void MythUIWebBrowser::SetHttpUserAgent(const QString &userAgent)
823{
824 if (!m_webEngine)
825 return;
826
827 m_webEngine->page()->profile()->setHttpUserAgent(userAgent);
828}
829
837{
838 if (!m_webEngine)
839 return;
840
841 color.setAlpha(255);
842 QPalette palette = m_webEngine->palette();
843 palette.setBrush(QPalette::Window, QBrush(color));
844 palette.setBrush(QPalette::Base, QBrush(color));
845 m_webEngine->setPalette(palette);
846
847 UpdateBuffer();
848}
849
861{
862 if (!m_webEngine)
863 return;
864
865 if (m_active == active)
866 return;
867
868 m_active = active;
869 m_wasActive = active;
870
871 if (m_active)
872 {
873 m_webEngine->setUpdatesEnabled(false);
874 m_webEngine->setParent(GetMythMainWindow()->GetPaintWindow());
875
876 if (m_hasFocus)
877 m_webEngine->setFocus();
878
879 m_webEngine->show();
880 m_webEngine->raise();
881 m_webEngine->setUpdatesEnabled(true);
882 }
883 else
884 {
885 UpdateBuffer();
886
887 m_webEngine->setParent(GetMythMainWindow());
888 m_webEngine->clearFocus();
889 }
890}
891
896{
897 SetZoom(m_zoom + 0.1);
898}
899
904{
905 SetZoom(m_zoom - 0.1);
906}
907
913{
914 if (!m_webEngine)
915 return;
916
917 m_zoom = std::clamp(zoom, 0.3, 5.0);
918 m_webEngine->setZoomFactor(m_zoom);
919
921 UpdateBuffer();
922
923 slotStatusBarMessage(tr("Zoom: %1%").arg(m_zoom * 100));
924
925 gCoreContext->SaveSetting("WebBrowserZoomLevel", QString("%1").arg(m_zoom));
926}
927
928void MythUIWebBrowser::Reload(bool useCache)
929{
930 if (useCache)
931 TriggerPageAction(QWebEnginePage::Reload);
932 else
933 TriggerPageAction(QWebEnginePage::QWebEnginePage::ReloadAndBypassCache);
934}
935
936void MythUIWebBrowser::TriggerPageAction(QWebEnginePage::WebAction action, bool checked)
937{
938 if (!m_webEngine)
939 return;
940
941 m_webEngine->triggerPageAction(action, checked);
942}
943
945{
946 if (!saveDir.isEmpty())
947 m_defaultSaveDir = saveDir;
948 else
949 m_defaultSaveDir = GetConfDir() + "/MythBrowser/";
950}
951
953{
954 if (!filename.isEmpty())
956 else
957 m_defaultSaveFilename.clear();
958}
959
965{
966 return m_zoom;
967}
968
975{
976 if (!m_webEngine)
977 return false;
978
979 return m_webEngine->history()->canGoForward();
980}
981
988{
989 if (!m_webEngine)
990 return false;
991
992 return m_webEngine->history()->canGoBack();
993}
994
999{
1000 if (!m_webEngine)
1001 return;
1002
1003 m_webEngine->back();
1004}
1005
1010{
1011 if (!m_webEngine)
1012 return;
1013
1014 m_webEngine->forward();
1015}
1016
1022{
1023 if (m_webEngine)
1024 return m_webEngine->icon();
1025
1026 return {};
1027}
1028
1034{
1035 if (m_webEngine)
1036 return m_webEngine->url();
1037
1038 return {};
1039}
1040
1046{
1047 if (m_webEngine)
1048 return m_webEngine->title();
1049
1050 return {""};
1051}
1052
1057void MythUIWebBrowser::RunJavaScript(const QString &scriptSource)
1058{
1059 if (m_webEngine)
1060 m_webEngine->page()->runJavaScript(scriptSource);
1061}
1062
1064{
1066}
1067
1069{
1070 SetActive(true);
1071 m_webEngine->setFocus();
1072}
1073
1075{
1076 GetMythMainWindow()->GetPaintWindow()->setFocus();
1077 m_webEngine->clearFocus();
1078}
1079
1080void MythUIWebBrowser::slotRenderProcessTerminated(QWebEnginePage::RenderProcessTerminationStatus terminationStatus, int exitCode)
1081{
1082 LOG(VB_GENERAL, LOG_INFO, QString("MythUIWebBrowser::slotRenderProcessTerminated - terminationStatus: %1, exitStatus: %2").arg(terminationStatus).arg(exitCode));
1083 m_webEngine->reload();
1084}
1085
1086void MythUIWebBrowser::slotFullScreenRequested(QWebEngineFullScreenRequest fullScreenRequest)
1087{
1088 fullScreenRequest.accept();
1089}
1090
1092{
1093 LOG(VB_GENERAL, LOG_INFO, QString("MythUIWebBrowser::slotWindowCloseRequested called"));
1094
1095 Back();
1096}
1097
1098void MythUIWebBrowser::MythUIWebBrowser::slotLoadStarted(void)
1099{
1100 ResetScrollBars();
1101
1102 emit loadStarted();
1103}
1104
1106{
1107 UpdateBuffer();
1108 emit loadFinished(ok);
1109}
1110
1112{
1113 emit loadProgress(progress);
1114}
1115
1116void MythUIWebBrowser::slotTitleChanged(const QString &title)
1117{
1118 emit titleChanged(title);
1119}
1120
1122{
1123 emit statusBarMessage(text);
1124}
1125
1127{
1128 LoadPage(url);
1129}
1130
1132{
1133 emit iconChanged(icon);
1134}
1135
1137{
1138 emit iconUrlChanged(url);
1139}
1140
1141void MythUIWebBrowser::slotScrollPositionChanged(const QPointF /*position*/)
1142{
1144}
1145
1147{
1149}
1150
1152{
1153// bool wasActive = (m_wasActive || m_active);
1154// SetActive(false);
1155// m_wasActive = wasActive;
1156}
1157
1159{
1160// SetActive(m_wasActive);
1161// slotTopScreenChanged(nullptr);
1162}
1163
1165{
1166 UpdateBuffer();
1167
1168 if (IsOnTopScreen())
1170 else
1171 {
1172 bool wasActive = (m_wasActive || m_active);
1173 SetActive(false);
1174 m_wasActive = wasActive;
1175 }
1176}
1177
1180{
1181 if (!m_parentScreen)
1182 return false;
1183
1184 for (int x = GetMythMainWindow()->GetStackCount() - 1; x >= 0; x--)
1185 {
1187
1188 // ignore stacks with no screens on them
1189 if (!stack->GetTopScreen())
1190 continue;
1191
1192 return (stack->GetTopScreen() == m_parentScreen);
1193 }
1194
1195 return false;
1196}
1197
1198
1200{
1201 if (!m_webEngine)
1202 return;
1203
1204 QPoint position = m_webEngine->page()->scrollPosition().toPoint();
1206 {
1207 int maximum = m_webEngine->page()->contentsSize().height() - m_actualBrowserArea.height();
1211 }
1212
1214 {
1215 int maximum = m_webEngine->page()->contentsSize().width() - m_actualBrowserArea.width();
1219 }
1220}
1221
1223{
1225
1226 if (!m_image || !m_webEngine)
1227 {
1228 LOG(VB_GENERAL, LOG_ERR, QString("MythUIWebBrowser::UpdateBuffer called - m_image or m_webEngine is FALSE"));
1229 return;
1230 }
1231
1232 if (!m_active || (m_active && m_webEngine->hasFocus()))
1233 {
1234 m_webEngine->render(m_image);
1236 Refresh();
1237 }
1238}
1239
1244{
1245 //SetRedraw();
1246
1247 if (!m_webEngine)
1248 return;
1249
1251 {
1252 UpdateBuffer();
1253 m_lastUpdateTime.start();
1254 }
1255
1257}
1258
1262void MythUIWebBrowser::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1263 int alphaMod, QRect clipRect)
1264{
1265 if (!m_image || m_image->isNull() || !m_webEngine || m_webEngine->hasFocus())
1266 return;
1267
1268 QRect area = m_actualBrowserArea;
1269 area.translate(xoffset, yoffset);
1270
1271 p->SetClipRect(clipRect);
1272 p->DrawImage(area.x(), area.y(), m_image, alphaMod);
1273}
1274
1276{
1277 int step = 5;
1278
1279 // speed up mouse movement if the same key is held down
1280 if (action == m_lastMouseAction &&
1281 m_lastMouseActionTime.isValid() &&
1282 !m_lastMouseActionTime.hasExpired(500))
1283 {
1284 m_lastMouseActionTime.start();
1286
1287 if (m_mouseKeyCount > 5)
1288 step = 25;
1289 }
1290 else
1291 {
1293 m_lastMouseActionTime.start();
1294 m_mouseKeyCount = 1;
1295 }
1296
1297 if (action == "MOUSEUP")
1298 {
1299 QPoint curPos = QCursor::pos();
1300 QCursor::setPos(curPos.x(), curPos.y() - step);
1301 }
1302 else if (action == "MOUSELEFT")
1303 {
1304 QPoint curPos = QCursor::pos();
1305 QCursor::setPos(curPos.x() - step, curPos.y());
1306 }
1307 else if (action == "MOUSERIGHT")
1308 {
1309 QPoint curPos = QCursor::pos();
1310 QCursor::setPos(curPos.x() + step, curPos.y());
1311 }
1312 else if (action == "MOUSEDOWN")
1313 {
1314 QPoint curPos = QCursor::pos();
1315 QCursor::setPos(curPos.x(), curPos.y() + step);
1316 }
1317 else if (action == "MOUSELEFTBUTTON")
1318 {
1319 QPoint curPos = QCursor::pos();
1320 QWidget *widget = QApplication::widgetAt(curPos);
1321
1322 if (widget)
1323 {
1324 curPos = widget->mapFromGlobal(curPos);
1325
1326 auto *me = new QMouseEvent(QEvent::MouseButtonPress, curPos, curPos,
1327 Qt::LeftButton, Qt::LeftButton,
1328 Qt::NoModifier);
1329 //FIXME
1330 QCoreApplication::postEvent(widget, me);
1331
1332 me = new QMouseEvent(QEvent::MouseButtonRelease, curPos, curPos,
1333 Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1334 //FIXME
1335 QCoreApplication::postEvent(widget, me);
1336 }
1337 }
1338}
1339
1341{
1343 {
1346 }
1347
1349 {
1352 }
1353}
1354
1359 const QString &filename, QDomElement &element, bool showWarnings)
1360{
1361 if (element.tagName() == "zoom")
1362 {
1363 QString zoom = getFirstText(element);
1364 m_zoom = zoom.toDouble();
1365 }
1366 else if (element.tagName() == "url")
1367 {
1368 m_widgetUrl.setUrl(getFirstText(element));
1369 }
1370 else if (element.tagName() == "userstylesheet")
1371 {
1372 m_userCssFile = getFirstText(element);
1373 }
1374 else if (element.tagName() == "updateinterval")
1375 {
1376 QString interval = getFirstText(element);
1377 m_updateInterval = interval.toInt();
1378 }
1379 else if (element.tagName() == "background")
1380 {
1381 m_bgColor = QColor(element.attribute("color", "#ffffff"));
1382 int alpha = element.attribute("alpha", "255").toInt();
1383 m_bgColor.setAlpha(alpha);
1384 }
1385 else if (element.tagName() == "browserarea")
1386 {
1387 m_browserArea = parseRect(element);
1388 }
1389 else if (element.tagName() == "scrollduration")
1390 {
1391 // this is no longer used QWebEngine has its own scroll animation
1392 }
1393 else if (element.tagName() == "acceptsfocus")
1394 {
1395 SetCanTakeFocus(parseBool(element));
1396 }
1397 else
1398 {
1399 return MythUIType::ParseElement(filename, element, showWarnings);
1400 }
1401
1402 return true;
1403}
1404
1409{
1410 auto *browser = dynamic_cast<MythUIWebBrowser *>(base);
1411 if (!browser)
1412 {
1413 LOG(VB_GENERAL, LOG_ERR, "ERROR, bad parsing");
1414 return;
1415 }
1416
1418
1419 m_browserArea = browser->m_browserArea;
1420 m_zoom = browser->m_zoom;
1421 m_bgColor = browser->m_bgColor;
1422 m_widgetUrl = browser->m_widgetUrl;
1423 m_userCssFile = browser->m_userCssFile;
1424 m_updateInterval = browser->m_updateInterval;
1425 m_defaultSaveDir = browser->m_defaultSaveDir;
1426 m_defaultSaveFilename = browser->m_defaultSaveFilename;
1427 //m_scrollAnimation.setDuration(browser->m_scrollAnimation.duration());
1428}
1429
1434{
1435 auto *browser = new MythUIWebBrowser(parent, objectName());
1436 browser->CopyFrom(this);
1437}
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:97
void SetCanTakeFocus(bool set=true)
Set whether this widget can take focus.
Definition: mythuitype.cpp:348
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:299
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:442
void Hide(void)
void Refresh(void)
bool m_hasFocus
Definition: mythuitype.h:275
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:288
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 pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
static std::vector< uint32_t > back
Definition: goom_core.cpp:27
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:285
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:201
QString m_mimeType
QString m_extension
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27