MythTV  master
mythuiwebbrowser.cpp
Go to the documentation of this file.
1 
7 #include "mythuiwebbrowser.h"
8 
9 // myth
10 #include "mythpainter.h"
11 #include "mythimage.h"
12 #include "mythmainwindow.h"
13 #include "mythfontproperties.h"
14 #include "mythlogging.h"
15 #include "mythdb.h"
16 #include "mythdirs.h"
17 #include "mythuihelper.h"
18 #include "mythcorecontext.h"
19 #include "mythdownloadmanager.h"
20 #include "mythdialogbox.h"
21 #include "mythprogressdialog.h"
22 #include "mythuiscrollbar.h"
23 
24 // qt
25 #include <QApplication>
26 #include <QWebFrame>
27 #include <QWebHistory>
28 #include <QPainter>
29 #include <QDir>
30 #include <QBuffer>
31 #include <QStyle>
32 #include <QKeyEvent>
33 #include <QDomDocument>
34 #include <QNetworkCookieJar>
35 #include <QNetworkConfiguration>
36 
37 #include <chrono> // for milliseconds
38 #include <thread> // for sleep_for
39 
40 struct MimeType
41 {
42  QString m_mimeType;
43  QString m_extension;
44  bool m_isVideo;
45 };
46 
48 {
49  { "audio/mpeg3", "mp3", false },
50  { "audio/x-mpeg-3", "mp3", false },
51  { "audio/mpeg", "mp2", false },
52  { "audio/x-mpeg", "mp2", false },
53  { "audio/ogg", "ogg", false },
54  { "audio/ogg", "oga", false },
55  { "audio/flac", "flac", false },
56  { "audio/x-ms-wma", "wma", false },
57  { "audio/wav", "wav", false },
58  { "audio/x-wav", "wav", false },
59  { "audio/ac3", "ac3", false },
60  { "audio/x-ac3", "ac3", false },
61  { "audio/x-oma", "oma", false },
62  { "audio/x-realaudio", "ra", false },
63  { "audio/dts", "dts", false },
64  { "audio/x-dts", "dts", false },
65  { "audio/aac", "aac", false },
66  { "audio/x-aac", "aac", false },
67  { "audio/m4a", "m4a", false },
68  { "audio/x-m4a", "m4a", false },
69  { "video/mpeg", "mpg", true },
70  { "video/mpeg", "mpeg", true },
71  { "video/x-ms-wmv", "wmv", true },
72  { "video/x-ms-wmv", "avi", true },
73  { "application/x-troff-msvideo", "avi", true },
74  { "video/avi", "avi", true },
75  { "video/msvideo", "avi", true },
76  { "video/x-msvideo", "avi", true }
77 };
78 
80  sizeof(SupportedMimeTypes[0]);
81 
82 QNetworkReply* MythNetworkAccessManager::createRequest(Operation op, const QNetworkRequest& req, QIODevice* outgoingData)
83 {
84  QNetworkReply* reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
85  reply->ignoreSslErrors();
86  return reply;
87 }
88 
90 
91 static void DestroyNetworkAccessManager(void)
92 {
93  if (networkManager)
94  {
95  delete networkManager;
96  networkManager = nullptr;
97  }
98 }
99 
100 static QNetworkAccessManager *GetNetworkAccessManager(void)
101 {
102  if (networkManager)
103  return networkManager;
104 
106 // This next line prevents seg fault at program exit in
107 // QNetworkConfiguration::~QNetworkConfiguration()
108 // when destructor is called by DestroyNetworkAccessManager
109  networkManager->setConfiguration(networkManager->configuration());
110  LOG(VB_GENERAL, LOG_DEBUG, "Copying DLManager's Cookie Jar");
111  GetMythDownloadManager()->loadCookieJar(GetConfDir() + "/MythBrowser/cookiejar.txt");
112  networkManager->setCookieJar(GetMythDownloadManager()->copyCookieJar());
113 
115 
116  return networkManager;
117 }
118 
124 BrowserApi::BrowserApi(QObject *parent)
125  : QObject(parent),
126  m_frame(nullptr), m_gotAnswer(false)
127 {
128  gCoreContext->addListener(this);
129 }
130 
132 {
134 }
135 
136 void BrowserApi::setWebView(QWebView *view)
137 {
138  QWebPage *page = view->page();
139  m_frame = page->mainFrame();
140 
141  attachObject();
142  connect(m_frame, SIGNAL(javaScriptWindowObjectCleared()), this,
143  SLOT(attachObject()));
144 }
145 
147 {
148  m_frame->addToJavaScriptWindowObject(QString("MusicPlayer"), this);
149 }
150 
152 {
153  MythEvent me(QString("MUSIC_COMMAND %1 PLAY").arg(gCoreContext->GetHostName()));
154  gCoreContext->dispatch(me);
155 }
156 
158 {
159  MythEvent me(QString("MUSIC_COMMAND %1 STOP").arg(gCoreContext->GetHostName()));
160  gCoreContext->dispatch(me);
161 }
162 
164 {
165  MythEvent me(QString("MUSIC_COMMAND %1 PAUSE %1").arg(gCoreContext->GetHostName()));
166  gCoreContext->dispatch(me);
167 }
168 
169 void BrowserApi::SetVolume(int volumn)
170 {
171  MythEvent me(QString("MUSIC_COMMAND %1 SET_VOLUME %2")
172  .arg(gCoreContext->GetHostName()).arg(volumn));
173  gCoreContext->dispatch(me);
174 }
175 
177 {
178  m_gotAnswer = false;
179 
180  MythEvent me(QString("MUSIC_COMMAND %1 GET_VOLUME")
181  .arg(gCoreContext->GetHostName()));
182  gCoreContext->dispatch(me);
183 
184  QElapsedTimer timer;
185  timer.start();
186 
187  while (!timer.hasExpired(2000) && !m_gotAnswer)
188  {
189  qApp->processEvents();
190  std::this_thread::sleep_for(std::chrono::milliseconds(10));
191  }
192 
193  if (m_gotAnswer)
194  return m_answer.toInt();
195 
196  return -1;
197 }
198 
199 void BrowserApi::PlayFile(const QString& filename)
200 {
201  MythEvent me(QString("MUSIC_COMMAND %1 PLAY_FILE '%2'")
202  .arg(gCoreContext->GetHostName()).arg(filename));
203  gCoreContext->dispatch(me);
204 }
205 
206 void BrowserApi::PlayTrack(int trackID)
207 {
208  MythEvent me(QString("MUSIC_COMMAND %1 PLAY_TRACK %2")
209  .arg(gCoreContext->GetHostName()).arg(trackID));
210  gCoreContext->dispatch(me);
211 }
212 
213 void BrowserApi::PlayURL(const QString& url)
214 {
215  MythEvent me(QString("MUSIC_COMMAND %1 PLAY_URL %2")
216  .arg(gCoreContext->GetHostName()).arg(url));
217  gCoreContext->dispatch(me);
218 }
219 
221 {
222  m_gotAnswer = false;
223 
224  MythEvent me(QString("MUSIC_COMMAND %1 GET_METADATA")
225  .arg(gCoreContext->GetHostName()));
226  gCoreContext->dispatch(me);
227 
228  QElapsedTimer timer;
229  timer.start();
230 
231  while (!timer.hasExpired(2000) && !m_gotAnswer)
232  {
233  qApp->processEvents();
234  std::this_thread::sleep_for(std::chrono::milliseconds(10));
235  }
236 
237  if (m_gotAnswer)
238  return m_answer;
239 
240  return QString("unknown");
241 }
242 
243 void BrowserApi::customEvent(QEvent *e)
244 {
245  if (e->type() == MythEvent::MythEventMessage)
246  {
247  auto *me = dynamic_cast<MythEvent *>(e);
248  if (me == nullptr)
249  return;
250 
251  const QString& message = me->Message();
252 
253  if (!message.startsWith("MUSIC_CONTROL"))
254  return;
255 
256  QStringList tokens = message.simplified().split(" ");
257 
258  if ((tokens.size() >= 4) && (tokens[1] == "ANSWER")
259  && (tokens[2] == gCoreContext->GetHostName()))
260  {
261  m_answer = tokens[3];
262 
263  for (int i = 4; i < tokens.size(); i++)
264  m_answer += QString(" ") + tokens[i];
265 
266  m_gotAnswer = true;
267  }
268  }
269 }
270 
271 MythWebPage::MythWebPage(QObject *parent)
272  : QWebPage(parent)
273 {
274  setNetworkAccessManager(GetNetworkAccessManager());
275 }
276 
278 {
279  LOG(VB_GENERAL, LOG_DEBUG, "Refreshing DLManager's Cookie Jar");
281  GetMythDownloadManager()->saveCookieJar(GetConfDir() + "/MythBrowser/cookiejar.txt");
282 }
283 
284 bool MythWebPage::supportsExtension(Extension extension) const
285 {
286  return extension == QWebPage::ErrorPageExtension;
287 }
288 
289 bool MythWebPage::extension(Extension extension, const ExtensionOption *option,
290  ExtensionReturn *output)
291 {
292  if (extension == QWebPage::ErrorPageExtension)
293  {
294  if (!option || !output)
295  return false;
296 
297  // Using static_cast yields the clang-tidy warning: do not use
298  // static_cast to downcast from a base to a derived class; use
299  // dynamic_cast instead. Using dynamic-cast yields the
300  // compiler error: 'QWebPage::ExtensionOption' is not
301  // polymorphic.
302  //
303  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
304  const auto *erroroption = static_cast<const ErrorPageExtensionOption *>(option);
305  ErrorPageExtensionReturn *erroroutput = nullptr;
306  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
307  erroroutput = static_cast<ErrorPageExtensionReturn *>(output);
308 
309  QString filename = "htmls/notfound.html";
310 
311  if (!GetMythUI()->FindThemeFile(filename))
312  return false;
313 
314  QFile file(QLatin1String(qPrintable(filename)));
315  bool isOpened = file.open(QIODevice::ReadOnly);
316 
317  if (!isOpened)
318  return false;
319 
320  QString title = tr("Error loading page: %1").arg(erroroption->url.toString());
321  QString html = QString(QLatin1String(file.readAll()))
322  .arg(title)
323  .arg(erroroption->errorString)
324  .arg(erroroption->url.toString());
325 
326  QBuffer imageBuffer;
327  imageBuffer.open(QBuffer::ReadWrite);
328  QIcon icon = qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning,
329  nullptr, nullptr);
330  QPixmap pixmap = icon.pixmap(QSize(32, 32));
331 
332  if (pixmap.save(&imageBuffer, "PNG"))
333  {
334  html.replace(QLatin1String("IMAGE_BINARY_DATA_HERE"),
335  QString(QLatin1String(imageBuffer.buffer().toBase64())));
336  }
337 
338  erroroutput->content = html.toUtf8();
339 
340  return true;
341  }
342 
343  return false;
344 }
345 
346 QString MythWebPage::userAgentForUrl(const QUrl &url) const
347 {
348  return QWebPage::userAgentForUrl(url).replace("Safari", "MythBrowser");
349 }
350 
356 MythWebView::MythWebView(QWidget *parent, MythUIWebBrowser *parentBrowser)
357  : QWebView(parent),
358  m_webpage(new MythWebPage(this))
359 {
360  setPage(m_webpage);
361 
362  m_parentBrowser = parentBrowser;
363 
364  connect(page(), SIGNAL(unsupportedContent(QNetworkReply *)),
365  this, SLOT(handleUnsupportedContent(QNetworkReply *)));
366 
367  connect(page(), SIGNAL(downloadRequested(const QNetworkRequest &)),
368  this, SLOT(handleDownloadRequested(QNetworkRequest)));
369 
370  page()->setForwardUnsupportedContent(true);
371 
372  m_api = new BrowserApi(this);
373  m_api->setWebView(this);
374 }
375 
377 {
378  delete m_webpage;
379  delete m_api;
380 }
381 
386 const char *kgetType = "\
387 function activeElement()\
388 {\
389  var type;\
390  type = document.activeElement.type;\
391  return type;\
392 }\
393 activeElement();";
394 
395 void MythWebView::keyPressEvent(QKeyEvent *event)
396 {
397  // does an edit have focus?
398  QString type = m_parentBrowser->evaluateJavaScript(QString(kgetType))
399  .toString().toLower();
400  bool editHasFocus = (type == "text" || type == "textarea" ||
401  type == "password");
402 
403  // if the QWebView widget has focus then all keypresses from a regular
404  // keyboard get sent here first
405  if (editHasFocus || m_parentBrowser->IsInputToggled())
406  {
407  // input is toggled so pass all keypresses to the QWebView's handler
408  QWebView::keyPressEvent(event);
409  }
410  else
411  {
412  // we need to convert a few keypress events so the QWebView does the
413  // right thing
414  QStringList actions;
415  bool handled = false;
416  handled = GetMythMainWindow()->TranslateKeyPress("Browser", event,
417  actions);
418 
419  for (int i = 0; i < actions.size() && !handled; i++)
420  {
421  QString action = actions[i];
422  handled = true;
423 
424  if (action == "NEXTLINK")
425  {
426  QKeyEvent tabKey(event->type(), Qt::Key_Tab,
427  event->modifiers(), QString(),
428  event->isAutoRepeat(), event->count());
429  *event = tabKey;
430  QWebView::keyPressEvent(event);
431  return;
432  }
433  if (action == "PREVIOUSLINK")
434  {
435  QKeyEvent shiftTabKey(event->type(), Qt::Key_Tab,
436  event->modifiers() | Qt::ShiftModifier,
437  QString(),
438  event->isAutoRepeat(), event->count());
439  *event = shiftTabKey;
440  QWebView::keyPressEvent(event);
441  return;
442  }
443  if (action == "FOLLOWLINK")
444  {
445  QKeyEvent returnKey(event->type(), Qt::Key_Return,
446  event->modifiers(), QString(),
447  event->isAutoRepeat(), event->count());
448  *event = returnKey;
449  QWebView::keyPressEvent(event);
450  return;
451  }
452  }
453 
454  // pass the keyPress event to our main window handler so they get
455  // handled properly by the various mythui handlers
456  QCoreApplication::postEvent(GetMythMainWindow(), new QKeyEvent(*event));
457  }
458 }
459 
460 void MythWebView::handleUnsupportedContent(QNetworkReply *reply)
461 {
462  if (reply->error() == QNetworkReply::NoError)
463  {
464  stop();
465 
466  QVariant header = reply->header(QNetworkRequest::ContentTypeHeader);
467 
468  if (header != QVariant())
469  {
470  LOG(VB_GENERAL, LOG_ERR,
471  QString("MythWebView::handleUnsupportedContent - %1")
472  .arg(header.toString()));
473  }
474 
475  m_downloadReply = reply;
476  m_downloadRequest = reply->request();
477  m_downloadAndPlay = false;
479 
480  return;
481  }
482 }
483 
484 void MythWebView::handleDownloadRequested(const QNetworkRequest &request)
485 {
486  m_downloadReply = nullptr;
487  doDownloadRequested(request);
488 }
489 
490 void MythWebView::doDownloadRequested(const QNetworkRequest &request)
491 {
492  m_downloadRequest = request;
493 
494  // get the filename from the url if available
495  QFileInfo fi(request.url().path());
496  QString basename(fi.completeBaseName());
497  QString extension = fi.suffix().toLower();
498  QString mimetype = getReplyMimetype();
499 
500  // if we have a default filename use that
501  QString saveBaseName = basename;
502 
503  if (!m_parentBrowser->GetDefaultSaveFilename().isEmpty())
504  {
505  QFileInfo savefi(m_parentBrowser->GetDefaultSaveFilename());
506  saveBaseName = savefi.completeBaseName();
507  }
508 
509  // if the filename is still empty use a default name
510  if (saveBaseName.isEmpty())
511  saveBaseName = "unnamed_download";
512 
513  // if we don't have an extension from the filename get one from the mime type
514  if (extension.isEmpty())
515  extension = getExtensionForMimetype(mimetype);
516 
517  if (!extension.isEmpty())
518  extension = '.' + extension;
519 
520  QString saveFilename = QString("%1%2%3")
522  .arg(saveBaseName)
523  .arg(extension);
524 
525  // dont overwrite an existing file
526  if (QFile::exists(saveFilename))
527  {
528  int i = 1;
529 
530  do
531  {
532  saveFilename = QString("%1%2-%3%4")
534  .arg(saveBaseName)
535  .arg(QString::number(i++))
536  .arg(extension);
537  }
538  while (QFile::exists(saveFilename));
539  }
540 
541  // if we are downloading and then playing the file don't ask for the file name
542  if (m_downloadAndPlay)
543  {
544  doDownload(saveFilename);
545  }
546  else
547  {
548  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
549 
550  QString msg = tr("Enter filename to save file");
551  auto *input = new MythTextInputDialog(popupStack, msg, FilterNone,
552  false, saveFilename);
553 
554  if (input->Create())
555  {
556  input->SetReturnEvent(this, "filenamedialog");
557  popupStack->AddScreen(input);
558  }
559  else
560  delete input;
561  }
562 }
563 
564 void MythWebView::doDownload(const QString &saveFilename)
565 {
566  if (saveFilename.isEmpty())
567  return;
568 
569  openBusyPopup(tr("Downloading file. Please wait..."));
570 
571  // No need to make sure the path to saveFilename exists because
572  // MythDownloadManage takes care of that
574  saveFilename, this);
575 }
576 
577 void MythWebView::openBusyPopup(const QString &message)
578 {
579  if (m_busyPopup)
580  return;
581 
582  QString msg(tr("Downloading..."));
583 
584  if (!message.isEmpty())
585  msg = message;
586 
587  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
588  m_busyPopup = new MythUIBusyDialog(msg, popupStack, "downloadbusydialog");
589 
590  if (m_busyPopup->Create())
591  popupStack->AddScreen(m_busyPopup, false);
592 }
593 
595 {
596  if (m_busyPopup)
597  m_busyPopup->Close();
598 
599  m_busyPopup = nullptr;
600 }
601 
602 void MythWebView::customEvent(QEvent *event)
603 {
604  if (event->type() == DialogCompletionEvent::kEventType)
605  {
606  auto *dce = (DialogCompletionEvent *)(event);
607 
608  // make sure the user didn't ESCAPE out of the dialog
609  if (dce->GetResult() < 0)
610  return;
611 
612  QString resultid = dce->GetId();
613  QString resulttext = dce->GetResultText();
614 
615  if (resultid == "filenamedialog")
616  doDownload(resulttext);
617  else if (resultid == "downloadmenu")
618  {
619  if (resulttext == tr("Play the file"))
620  {
621  QFileInfo fi(m_downloadRequest.url().path());
622  QString basename(fi.baseName());
623  QString extension = fi.suffix();
624  QString mimeType = getReplyMimetype();
625 
626  if (isMusicFile(extension, mimeType))
627  {
628  MythEvent me(QString("MUSIC_COMMAND %1 PLAY_URL %2")
629  .arg(gCoreContext->GetHostName())
630  .arg(m_downloadRequest.url().toString()));
631  gCoreContext->dispatch(me);
632  }
633  else if (isVideoFile(extension, mimeType))
634  {
635  GetMythMainWindow()->HandleMedia("Internal",
636  m_downloadRequest.url().toString());
637  }
638  else
639  {
640  LOG(VB_GENERAL, LOG_ERR,
641  QString("MythWebView: Asked to play a file with "
642  "extension '%1' but don't know how")
643  .arg(extension));
644  }
645  }
646  else if (resulttext == tr("Download the file"))
647  {
649  }
650  else if (resulttext == tr("Download and play the file"))
651  {
652  m_downloadAndPlay = true;
654  }
655  }
656  }
657  else if (event->type() == MythEvent::MythEventMessage)
658  {
659  auto *me = dynamic_cast<MythEvent *>(event);
660  if (me == nullptr)
661  return;
662 
663  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
664  if (tokens.isEmpty())
665  return;
666 
667  if (tokens[0] == "DOWNLOAD_FILE")
668  {
669  QStringList args = me->ExtraDataList();
670 
671  if (tokens[1] == "UPDATE")
672  {
673  // could update a progressbar here
674  }
675  else if (tokens[1] == "FINISHED")
676  {
677  int fileSize = args[2].toInt();
678  int errorCode = args[4].toInt();
679  QString filename = args[1];
680 
681  closeBusyPopup();
682 
683  if ((errorCode != 0) || (fileSize == 0))
684  ShowOkPopup(tr("ERROR downloading file."));
685  else if (m_downloadAndPlay)
686  GetMythMainWindow()->HandleMedia("Internal", filename);
687 
688  MythEvent me2(QString("BROWSER_DOWNLOAD_FINISHED"), args);
689  gCoreContext->dispatch(me2);
690  }
691  }
692  }
693 }
694 
696 {
697  QFileInfo fi(m_downloadRequest.url().path());
698  QString basename(fi.baseName());
699  QString extension = fi.suffix();
700  QString mimeType = getReplyMimetype();
701 
702  QString label = tr("What do you want to do with this file?");
703 
704  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
705 
706  auto *menu = new MythDialogBox(label, popupStack, "downloadmenu");
707 
708  if (!menu->Create())
709  {
710  delete menu;
711  return;
712  }
713 
714  menu->SetReturnEvent(this, "downloadmenu");
715 
716  if (isMusicFile(extension, mimeType))
717  menu->AddButton(tr("Play the file"));
718 
719  if (isVideoFile(extension, mimeType))
720  menu->AddButton(tr("Download and play the file"));
721 
722  menu->AddButton(tr("Download the file"));
723  menu->AddButton(tr("Cancel"));
724 
725  popupStack->AddScreen(menu);
726 }
727 
728 QString MythWebView::getExtensionForMimetype(const QString &mimetype)
729 {
730  for (int x = 0; x < SupportedMimeTypesCount; x++)
731  {
732  if (!mimetype.isEmpty() && mimetype == SupportedMimeTypes[x].m_mimeType)
733  return SupportedMimeTypes[x].m_extension;
734  }
735 
736  return QString("");
737 }
738 
739 bool MythWebView::isMusicFile(const QString &extension, const QString &mimetype)
740 {
741  for (int x = 0; x < SupportedMimeTypesCount; x++)
742  {
743  if (!SupportedMimeTypes[x].m_isVideo)
744  {
745  if (!mimetype.isEmpty() &&
746  mimetype == SupportedMimeTypes[x].m_mimeType)
747  return true;
748 
749  if (!extension.isEmpty() &&
750  extension.toLower() == SupportedMimeTypes[x].m_extension)
751  return true;
752  }
753  }
754 
755  return false;
756 }
757 
758 bool MythWebView::isVideoFile(const QString &extension, const QString &mimetype)
759 {
760  for (int x = 0; x < SupportedMimeTypesCount; x++)
761  {
762  if (SupportedMimeTypes[x].m_isVideo)
763  {
764  if (!mimetype.isEmpty() &&
765  mimetype == SupportedMimeTypes[x].m_mimeType)
766  return true;
767 
768  if (!extension.isEmpty() &&
769  extension.toLower() == SupportedMimeTypes[x].m_extension)
770  return true;
771  }
772  }
773 
774  return false;
775 }
776 
778 {
779  if (!m_downloadReply)
780  return QString();
781 
782  QString mimeType;
783  QVariant header = m_downloadReply->header(QNetworkRequest::ContentTypeHeader);
784 
785  if (header != QVariant())
786  mimeType = header.toString();
787 
788  return mimeType;
789 }
790 
791 QWebView *MythWebView::createWindow(QWebPage::WebWindowType /* type */)
792 {
793  return (QWebView *) this;
794 }
795 
796 
836 MythUIWebBrowser::MythUIWebBrowser(MythUIType *parent, const QString &name)
837  : MythUIType(parent, name),
838  m_parentScreen(nullptr),
839  m_browser(nullptr), m_browserArea(MythRect()),
840  m_actualBrowserArea(MythRect()), m_image(nullptr),
841  m_active(false), m_wasActive(false),
842  m_initialized(false),
843  m_updateInterval(0), m_zoom(1.0),
844  m_bgColor("White"), m_widgetUrl(QUrl()), m_userCssFile(""),
845  m_defaultSaveDir(GetConfDir() + "/MythBrowser/"),
846  m_defaultSaveFilename(""),
847  m_inputToggled(false), m_lastMouseAction(""),
848  m_mouseKeyCount(0),
849  m_horizontalScrollbar(nullptr), m_verticalScrollbar(nullptr)
850 {
851  SetCanTakeFocus(true);
852  m_scrollAnimation.setDuration(0);
853  m_lastUpdateTime.start();
854 }
855 
860 {
862 
863  Init();
864 }
865 
874 {
875  // only do the initialisation for widgets not being stored in the global object store
876  if (parent() == GetGlobalObjectStore())
877  return;
878 
879  if (m_initialized)
880  return;
881 
884  m_actualBrowserArea.translate(m_Area.x(), m_Area.y());
885 
886  if (!m_actualBrowserArea.isValid())
888 
889  m_browser = new MythWebView(GetMythMainWindow()->GetPaintWindow(), this);
890  m_browser->setPalette(qApp->style()->standardPalette());
891  m_browser->setGeometry(m_actualBrowserArea);
892  m_browser->setFixedSize(m_actualBrowserArea.size());
894  m_browser->page()->setLinkDelegationPolicy(QWebPage::DontDelegateLinks);
895 
896  bool err = false;
897  UIUtilW::Assign(this, m_horizontalScrollbar, "horizscrollbar", &err);
898  UIUtilW::Assign(this, m_verticalScrollbar, "vertscrollbar", &err);
900  {
901  QWebFrame* frame = m_browser->page()->currentFrame();
902  frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
903  connect(m_horizontalScrollbar, SIGNAL(Hiding()),
904  this, SLOT(slotScrollBarHiding()));
905  connect(m_horizontalScrollbar, SIGNAL(Showing()),
906  this, SLOT(slotScrollBarShowing()));
907  }
908 
910  {
911  QWebFrame* frame = m_browser->page()->currentFrame();
912  frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
913  connect(m_verticalScrollbar, SIGNAL(Hiding()),
914  this, SLOT(slotScrollBarHiding()));
915  connect(m_verticalScrollbar, SIGNAL(Showing()),
916  this, SLOT(slotScrollBarShowing()));
917  }
918 
919  // if we have a valid css URL use that ...
920  if (!m_userCssFile.isEmpty())
921  {
922  QString filename = m_userCssFile;
923 
925  LoadUserStyleSheet(QUrl("file://" + filename));
926  }
927  else
928  {
929  // ...otherwise use the default one
930  QString filename = "htmls/mythbrowser.css";
931 
933  LoadUserStyleSheet(QUrl("file://" + filename));
934  }
935 
936  m_browser->winId();
937 
939 
940  connect(m_browser, SIGNAL(loadStarted()),
941  this, SLOT(slotLoadStarted()));
942  connect(m_browser, SIGNAL(loadFinished(bool)),
943  this, SLOT(slotLoadFinished(bool)));
944  connect(m_browser, SIGNAL(loadProgress(int)),
945  this, SLOT(slotLoadProgress(int)));
946  connect(m_browser, SIGNAL(titleChanged(const QString &)),
947  this, SLOT(slotTitleChanged(const QString &)));
948  connect(m_browser, SIGNAL(iconChanged(void)),
949  this, SLOT(slotIconChanged(void)));
950  connect(m_browser, SIGNAL(statusBarMessage(const QString &)),
951  this, SLOT(slotStatusBarMessage(const QString &)));
952  connect(m_browser->page(), SIGNAL(linkHovered(const QString &,
953  const QString &,
954  const QString &)),
955  this, SLOT(slotStatusBarMessage(const QString &)));
956  connect(m_browser, SIGNAL(linkClicked(const QUrl &)),
957  this, SLOT(slotLinkClicked(const QUrl &)));
958 
959  // find what screen we are on
960  m_parentScreen = nullptr;
961  QObject *parentObject = parent();
962 
963  while (parentObject)
964  {
965  m_parentScreen = dynamic_cast<MythScreenType *>(parentObject);
966 
967  if (m_parentScreen)
968  break;
969 
970  parentObject = parentObject->parent();
971  }
972 
973  if (!m_parentScreen && parent() != GetGlobalObjectStore())
974  LOG(VB_GENERAL, LOG_ERR,
975  "MythUIWebBrowser: failed to find our parent screen");
976 
977  // connect to the topScreenChanged signals on each screen stack
978  for (int x = 0; x < GetMythMainWindow()->GetStackCount(); x++)
979  {
981 
982  if (stack)
983  connect(stack, SIGNAL(topScreenChanged(MythScreenType *)),
984  this, SLOT(slotTopScreenChanged(MythScreenType *)));
985  }
986 
987  // set up the icon cache directory
988  QString path = GetConfDir();
989  QDir dir(path);
990 
991  if (!dir.exists())
992  dir.mkdir(path);
993 
994  path += "/MythBrowser";
995  dir.setPath(path);
996 
997  if (!dir.exists())
998  dir.mkdir(path);
999 
1000  QWebSettings::setIconDatabasePath(path);
1001 
1002  if (gCoreContext->GetNumSetting("WebBrowserEnablePlugins", 1) == 1)
1003  {
1004  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: enabling plugins");
1005  QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
1006  true);
1007  }
1008  else
1009  {
1010  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling plugins");
1011  QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
1012  false);
1013  }
1014 
1015  if (!gCoreContext->GetBoolSetting("WebBrowserEnableJavascript",true))
1016  {
1017  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling JavaScript");
1018  QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled, false);
1019  }
1020 
1021  QImage image = QImage(m_actualBrowserArea.size(), QImage::Format_ARGB32);
1023  m_image->Assign(image);
1024 
1026 
1027  m_zoom = gCoreContext->GetFloatSetting("WebBrowserZoomLevel", 1.0);
1028 
1029  SetZoom(m_zoom);
1030 
1031  if (!m_widgetUrl.isEmpty() && m_widgetUrl.isValid())
1033 
1034  m_initialized = true;
1035 }
1036 
1041 {
1042  if (m_browser)
1043  {
1044  m_browser->hide();
1045  m_browser->disconnect();
1046  m_browser->deleteLater();
1047  m_browser = nullptr;
1048  }
1049 
1050  if (m_image)
1051  {
1052  m_image->DecrRef();
1053  m_image = nullptr;
1054  }
1055 }
1056 
1061 void MythUIWebBrowser::LoadPage(const QUrl& url)
1062 {
1063  if (!m_browser)
1064  return;
1065 
1066  ResetScrollBars();
1067 
1068  m_browser->setUrl(url);
1069 }
1070 
1077 void MythUIWebBrowser::SetHtml(const QString &html, const QUrl &baseUrl)
1078 {
1079  if (!m_browser)
1080  return;
1081 
1082  ResetScrollBars();
1083 
1084  m_browser->setHtml(html, baseUrl);
1085 }
1086 
1092 {
1093  if (!m_browser)
1094  return;
1095 
1096  LOG(VB_GENERAL, LOG_INFO,
1097  "MythUIWebBrowser: Loading css from - " + url.toString());
1098 
1099  m_browser->page()->settings()->setUserStyleSheetUrl(url);
1100 }
1101 
1109 {
1110  if (!m_browser)
1111  return;
1112 
1113  color.setAlpha(255);
1114  QPalette palette = m_browser->page()->palette();
1115  palette.setBrush(QPalette::Window, QBrush(color));
1116  palette.setBrush(QPalette::Base, QBrush(color));
1117  m_browser->page()->setPalette(palette);
1118 
1119  UpdateBuffer();
1120 }
1121 
1133 {
1134  if (!m_browser)
1135  return;
1136 
1137  if (m_active == active)
1138  return;
1139 
1140  m_active = active;
1141  m_wasActive = active;
1142 
1143  if (m_active)
1144  {
1145  m_browser->setUpdatesEnabled(false);
1146  m_browser->setFocus();
1147  m_browser->show();
1148  m_browser->raise();
1149  if (qApp->platformName().contains("egl"))
1150  {
1151  m_browser->setParent(nullptr);
1152  m_browser->setFocus();
1153  m_browser->show();
1154  m_browser->raise();
1155  }
1156  m_browser->setUpdatesEnabled(true);
1157  }
1158  else
1159  {
1160  m_browser->clearFocus();
1161  m_browser->hide();
1162  if (qApp->platformName().contains("egl"))
1163  m_browser->setParent(GetMythMainWindow());
1164  UpdateBuffer();
1165  }
1166 }
1167 
1172 {
1173  SetZoom(m_zoom + 0.1F);
1174 }
1175 
1180 {
1181  SetZoom(m_zoom - 0.1F);
1182 }
1183 
1189 {
1190  if (!m_browser)
1191  return;
1192 
1193  if (zoom < 0.3F)
1194  zoom = 0.3F;
1195 
1196  if (zoom > 5.0F)
1197  zoom = 5.0F;
1198 
1199  m_zoom = zoom;
1200  m_browser->setZoomFactor(m_zoom);
1201  ResetScrollBars();
1202  UpdateBuffer();
1203 
1204  slotStatusBarMessage(tr("Zoom: %1%").arg(m_zoom * 100));
1205 
1206  gCoreContext->SaveSetting("WebBrowserZoomLevel", QString("%1").arg(m_zoom));
1207 }
1208 
1209 void MythUIWebBrowser::SetDefaultSaveDirectory(const QString &saveDir)
1210 {
1211  if (!saveDir.isEmpty())
1212  m_defaultSaveDir = saveDir;
1213  else
1214  m_defaultSaveDir = GetConfDir() + "/MythBrowser/";
1215 }
1216 
1218 {
1219  if (!filename.isEmpty())
1221  else
1222  m_defaultSaveFilename.clear();
1223 }
1224 
1230 {
1231  return m_zoom;
1232 }
1233 
1240 {
1241  if (!m_browser)
1242  return false;
1243 
1244  return m_browser->history()->canGoForward();
1245 }
1246 
1253 {
1254  if (!m_browser)
1255  return false;
1256 
1257  return m_browser->history()->canGoBack();
1258 }
1259 
1264 {
1265  if (!m_browser)
1266  return;
1267 
1268  m_browser->back();
1269 }
1270 
1275 {
1276  if (!m_browser)
1277  return;
1278 
1279  m_browser->forward();
1280 }
1281 
1287 {
1288  if (m_browser)
1289  {
1290  return QWebSettings::iconForUrl(m_browser->url());
1291  }
1292  return QIcon();
1293 }
1294 
1300 {
1301  if (m_browser)
1302  {
1303  return m_browser->url();
1304  }
1305  return QUrl();
1306 }
1307 
1313 {
1314  if (m_browser)
1315  return m_browser->title();
1316  return QString("");
1317 }
1318 
1323 QVariant MythUIWebBrowser::evaluateJavaScript(const QString &scriptSource)
1324 {
1325  if (m_browser)
1326  {
1327  QWebFrame *frame = m_browser->page()->currentFrame();
1328  return frame->evaluateJavaScript(scriptSource);
1329  }
1330  return QVariant();
1331 }
1332 
1333 void MythUIWebBrowser::Scroll(int dx, int dy)
1334 {
1335  if (!m_browser)
1336  return;
1337 
1338  QPoint startPos = m_browser->page()->currentFrame()->scrollPosition();
1339  QPoint endPos = startPos + QPoint(dx, dy);
1340 
1341  if (GetPainter()->SupportsAnimation() && m_scrollAnimation.duration() > 0)
1342  {
1343  // Previous scroll has been completed
1344  if (m_destinationScrollPos == startPos)
1345  m_scrollAnimation.setEasingCurve(QEasingCurve::InOutCubic);
1346  else
1347  m_scrollAnimation.setEasingCurve(QEasingCurve::OutCubic);
1348 
1349  m_destinationScrollPos = endPos;
1350  m_scrollAnimation.setStartValue(startPos);
1353  }
1354  else
1355  {
1356  m_destinationScrollPos = endPos;
1357  m_browser->page()->currentFrame()->setScrollPosition(endPos);
1358  UpdateBuffer();
1359  }
1360 }
1361 
1363 {
1364  ResetScrollBars();
1365  emit loadStarted();
1366 }
1367 
1369 {
1370  UpdateBuffer();
1371  emit loadFinished(ok);
1372 }
1373 
1375 {
1376  emit loadProgress(progress);
1377 }
1378 
1379 void MythUIWebBrowser::slotTitleChanged(const QString &title)
1380 {
1381  emit titleChanged(title);
1382 }
1383 
1385 {
1386  emit statusBarMessage(text);
1387 }
1388 
1390 {
1391  LoadPage(url);
1392 }
1393 
1395 {
1396  emit iconChanged();
1397 }
1398 
1400 {
1401  bool wasActive = (m_wasActive || m_active);
1402  SetActive(false);
1403  m_wasActive = wasActive;
1404 }
1405 
1407 {
1409  slotTopScreenChanged(nullptr);
1410 }
1411 
1413 {
1414  if (IsOnTopScreen())
1416  else
1417  {
1418  bool wasActive = (m_wasActive || m_active);
1419  SetActive(false);
1420  m_wasActive = wasActive;
1421  }
1422 }
1423 
1426 {
1427  if (!m_parentScreen)
1428  return false;
1429 
1430  for (int x = GetMythMainWindow()->GetStackCount() - 1; x >= 0; x--)
1431  {
1433 
1434  // ignore stacks with no screens on them
1435  if (!stack->GetTopScreen())
1436  continue;
1437 
1438  return (stack->GetTopScreen() == m_parentScreen);
1439  }
1440 
1441  return false;
1442 }
1443 
1444 
1446 {
1447  if (!m_browser)
1448  return;
1449 
1450  QPoint position = m_browser->page()->currentFrame()->scrollPosition();
1451  if (m_verticalScrollbar)
1452  {
1453  int maximum =
1454  m_browser->page()->currentFrame()->contentsSize().height() -
1455  m_actualBrowserArea.height();
1456  m_verticalScrollbar->SetMaximum(maximum);
1458  m_verticalScrollbar->SetSliderPosition(position.y());
1459  }
1460 
1462  {
1463  int maximum =
1464  m_browser->page()->currentFrame()->contentsSize().width() -
1465  m_actualBrowserArea.width();
1469  }
1470 }
1471 
1473 {
1474  UpdateScrollBars();
1475 
1476  if (!m_image || !m_browser)
1477  return;
1478 
1479  if (!m_active || (m_active && !m_browser->hasFocus()))
1480  {
1481  QPainter painter(m_image);
1482  m_browser->render(&painter);
1483  painter.end();
1484 
1485  m_image->SetChanged();
1486  Refresh();
1487  }
1488 }
1489 
1494 {
1495  if (!m_browser)
1496  return;
1497 
1498  if (m_scrollAnimation.IsActive() &&
1500  m_browser->page()->currentFrame()->scrollPosition())
1501  {
1503 
1504  QPoint scrollPosition = m_scrollAnimation.currentValue().toPoint();
1505  m_browser->page()->currentFrame()->setScrollPosition(scrollPosition);
1506 
1507  SetRedraw();
1508  UpdateBuffer();
1509  }
1510  else if (m_updateInterval && m_lastUpdateTime.hasExpired(m_updateInterval))
1511  {
1512  UpdateBuffer();
1513  m_lastUpdateTime.start();
1514  }
1515 
1517 }
1518 
1522 void MythUIWebBrowser::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1523  int alphaMod, QRect clipRect)
1524 {
1525  if (!m_image || m_image->isNull() || !m_browser || m_browser->hasFocus())
1526  return;
1527 
1528  QRect area = m_actualBrowserArea;
1529  area.translate(xoffset, yoffset);
1530 
1531  p->SetClipRect(clipRect);
1532  p->DrawImage(area.x(), area.y(), m_image, alphaMod);
1533 }
1534 
1538 bool MythUIWebBrowser::keyPressEvent(QKeyEvent *event)
1539 {
1540  if (!m_browser)
1541  return false;
1542 
1543  QStringList actions;
1544  bool handled = false;
1545  handled = GetMythMainWindow()->TranslateKeyPress("Browser", event, actions);
1546 
1547  for (int i = 0; i < actions.size() && !handled; i++)
1548  {
1549  QString action = actions[i];
1550  handled = true;
1551 
1552  if (action == "TOGGLEINPUT")
1553  {
1555 
1556  if (m_inputToggled)
1557  slotStatusBarMessage(tr("Sending key presses to web page"));
1558  else
1559  slotStatusBarMessage(tr("Sending key presses to MythTV"));
1560 
1561  return true;
1562  }
1563 
1564  // if input is toggled all input goes to the web page
1565  if (m_inputToggled)
1566  {
1567  m_browser->keyPressEvent(event);
1568 
1569  return true;
1570  }
1571 
1572  QWebFrame *frame = m_browser->page()->currentFrame();
1573  if (action == "UP")
1574  {
1575  int pos = frame->scrollPosition().y();
1576 
1577  if (pos > 0)
1578  {
1579  Scroll(0, -m_actualBrowserArea.height() / 10);
1580  }
1581  else
1582  handled = false;
1583  }
1584  else if (action == "DOWN")
1585  {
1586  int pos = frame->scrollPosition().y();
1587  QSize maximum = frame->contentsSize() - m_actualBrowserArea.size();
1588 
1589  if (pos != maximum.height())
1590  {
1591  Scroll(0, m_actualBrowserArea.height() / 10);
1592  }
1593  else
1594  handled = false;
1595  }
1596  else if (action == "LEFT")
1597  {
1598  int pos = frame->scrollPosition().x();
1599 
1600  if (pos > 0)
1601  {
1602  Scroll(-m_actualBrowserArea.width() / 10, 0);
1603  }
1604  else
1605  handled = false;
1606  }
1607  else if (action == "RIGHT")
1608  {
1609  int pos = frame->scrollPosition().x();
1610  QSize maximum = frame->contentsSize() - m_actualBrowserArea.size();
1611 
1612  if (pos != maximum.width())
1613  {
1614  Scroll(m_actualBrowserArea.width() / 10, 0);
1615  }
1616  else
1617  handled = false;
1618  }
1619  else if (action == "PAGEUP")
1620  {
1621  Scroll(0, -m_actualBrowserArea.height());
1622  }
1623  else if (action == "PAGEDOWN")
1624  {
1625  Scroll(0, m_actualBrowserArea.height());
1626  }
1627  else if (action == "ZOOMIN")
1628  {
1629  ZoomIn();
1630  }
1631  else if (action == "ZOOMOUT")
1632  {
1633  ZoomOut();
1634  }
1635  else if (action == "MOUSEUP" || action == "MOUSEDOWN" ||
1636  action == "MOUSELEFT" || action == "MOUSERIGHT" ||
1637  action == "MOUSELEFTBUTTON")
1638  {
1640  }
1641  else if (action == "PAGELEFT")
1642  {
1643  Scroll(-m_actualBrowserArea.width(), 0);
1644  }
1645  else if (action == "PAGERIGHT")
1646  {
1647  Scroll(m_actualBrowserArea.width(), 0);
1648  }
1649  else if ((action == "NEXTLINK") ||
1650  (action == "PREVIOUSLINK") ||
1651  (action == "FOLLOWLINK"))
1652  {
1653  m_browser->keyPressEvent(event);
1654  }
1655  else if (action == "HISTORYBACK")
1656  {
1657  Back();
1658  }
1659  else if (action == "HISTORYFORWARD")
1660  {
1661  Forward();
1662  }
1663  else
1664  handled = false;
1665  }
1666 
1667  return handled;
1668 }
1669 
1671 {
1672  int step = 5;
1673 
1674  // speed up mouse movement if the same key is held down
1675  if (action == m_lastMouseAction &&
1676  m_lastMouseActionTime.isValid() &&
1677  !m_lastMouseActionTime.hasExpired(500))
1678  {
1679  m_lastMouseActionTime.start();
1680  m_mouseKeyCount++;
1681 
1682  if (m_mouseKeyCount > 5)
1683  step = 25;
1684  }
1685  else
1686  {
1688  m_lastMouseActionTime.start();
1689  m_mouseKeyCount = 1;
1690  }
1691 
1692  if (action == "MOUSEUP")
1693  {
1694  QPoint curPos = QCursor::pos();
1695  QCursor::setPos(curPos.x(), curPos.y() - step);
1696  }
1697  else if (action == "MOUSELEFT")
1698  {
1699  QPoint curPos = QCursor::pos();
1700  QCursor::setPos(curPos.x() - step, curPos.y());
1701  }
1702  else if (action == "MOUSERIGHT")
1703  {
1704  QPoint curPos = QCursor::pos();
1705  QCursor::setPos(curPos.x() + step, curPos.y());
1706  }
1707  else if (action == "MOUSEDOWN")
1708  {
1709  QPoint curPos = QCursor::pos();
1710  QCursor::setPos(curPos.x(), curPos.y() + step);
1711  }
1712  else if (action == "MOUSELEFTBUTTON")
1713  {
1714  QPoint curPos = QCursor::pos();
1715  QWidget *widget = QApplication::widgetAt(curPos);
1716 
1717  if (widget)
1718  {
1719  curPos = widget->mapFromGlobal(curPos);
1720 
1721  auto *me = new QMouseEvent(QEvent::MouseButtonPress, curPos,
1722  Qt::LeftButton, Qt::LeftButton,
1723  Qt::NoModifier);
1724  QCoreApplication::postEvent(widget, me);
1725 
1726  me = new QMouseEvent(QEvent::MouseButtonRelease, curPos,
1727  Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1728  QCoreApplication::postEvent(widget, me);
1729  }
1730  }
1731 }
1732 
1734 {
1735  if (m_verticalScrollbar)
1736  {
1739  }
1740 
1742  {
1745  }
1746 }
1747 
1752  const QString &filename, QDomElement &element, bool showWarnings)
1753 {
1754  if (element.tagName() == "zoom")
1755  {
1756  QString zoom = getFirstText(element);
1757  m_zoom = zoom.toFloat();
1758  }
1759  else if (element.tagName() == "url")
1760  {
1761  m_widgetUrl.setUrl(getFirstText(element));
1762  }
1763  else if (element.tagName() == "userstylesheet")
1764  {
1765  m_userCssFile = getFirstText(element);
1766  }
1767  else if (element.tagName() == "updateinterval")
1768  {
1769  QString interval = getFirstText(element);
1770  m_updateInterval = interval.toInt();
1771  }
1772  else if (element.tagName() == "background")
1773  {
1774  m_bgColor = QColor(element.attribute("color", "#ffffff"));
1775  int alpha = element.attribute("alpha", "255").toInt();
1776  m_bgColor.setAlpha(alpha);
1777  }
1778  else if (element.tagName() == "browserarea")
1779  {
1780  m_browserArea = parseRect(element);
1781  }
1782  else if (element.tagName() == "scrollduration")
1783  {
1784  QString duration = getFirstText(element);
1785  m_scrollAnimation.setDuration(duration.toInt());
1786  }
1787  else if (element.tagName() == "acceptsfocus")
1788  {
1789  SetCanTakeFocus(parseBool(element));
1790  }
1791  else
1792  {
1793  return MythUIType::ParseElement(filename, element, showWarnings);
1794  }
1795 
1796  return true;
1797 }
1798 
1803 {
1804  auto *browser = dynamic_cast<MythUIWebBrowser *>(base);
1805  if (!browser)
1806  {
1807  LOG(VB_GENERAL, LOG_ERR, "ERROR, bad parsing");
1808  return;
1809  }
1810 
1811  MythUIType::CopyFrom(base);
1812 
1813  m_browserArea = browser->m_browserArea;
1814  m_zoom = browser->m_zoom;
1815  m_bgColor = browser->m_bgColor;
1816  m_widgetUrl = browser->m_widgetUrl;
1817  m_userCssFile = browser->m_userCssFile;
1818  m_updateInterval = browser->m_updateInterval;
1819  m_defaultSaveDir = browser->m_defaultSaveDir;
1820  m_defaultSaveFilename = browser->m_defaultSaveFilename;
1821  m_scrollAnimation.setDuration(browser->m_scrollAnimation.duration());
1822 }
1823 
1828 {
1829  auto *browser = new MythUIWebBrowser(parent, objectName());
1830  browser->CopyFrom(this);
1831 }
static QNetworkAccessManager * GetNetworkAccessManager(void)
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
MythWebView(QWidget *parent, MythUIWebBrowser *parentBrowser)
void CalculateArea(const MythRect &parentArea)
Definition: mythrect.cpp:32
void ZoomOut(void)
Decrease the text size.
void Scroll(int dx, int dy)
QString m_mimeType
MythUIWebBrowser * m_parentBrowser
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
~MythWebPage() override
virtual MythPainter * GetPainter(void)
void Pulse(void) override
Pulse is called 70 times a second to trigger a single frame of an animation.
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
void queueDownload(const QString &url, const QString &dest, QObject *caller, bool reload=false)
Adds a url to the download queue.
void LoadPage(const QUrl &url)
Loads the specified url and displays it.
void SetDefaultSaveFilename(const QString &filename)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
void iconChanged(void)
link hit test messages
QUrl GetUrl(void)
Gets the current page's url.
static void DestroyNetworkAccessManager(void)
virtual void SetChanged(bool change=true)
Definition: mythimage.h:50
void SaveSetting(const QString &key, int newValue)
static Type MythEventMessage
Definition: mythevent.h:73
void keyPressEvent(QKeyEvent *event) override
void SetRedraw(void)
Definition: mythuitype.cpp:293
void removeListener(QObject *listener)
Remove a listener to the observable.
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:55
MythScreenStack * GetStackAt(int pos)
void handleDownloadRequested(const QNetworkRequest &request)
Basic menu dialog, message and a list of options.
MythUIAnimation m_scrollAnimation
static MimeType SupportedMimeTypes[]
static bool isVideoFile(const QString &extension, const QString &mimetype)
Adds a JavaScript object.
void slotStatusBarMessage(const QString &text)
void HandleMouseAction(const QString &action)
MythUIScrollBar * m_verticalScrollbar
void customEvent(QEvent *e) override
MythScreenStack * GetStack(const QString &stackname)
QElapsedTimer m_lastMouseActionTime
static void Stop(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void SetPageStep(int value)
void addListener(QObject *listener)
Add a listener to the observable.
bool Create(void) override
void slotLinkClicked(const QUrl &url)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void Forward(void)
Got forward in page history.
MythRect m_actualBrowserArea
static MythThemedMenu * menu
void Init(void)
Initializes the widget ready for use.
static QString getExtensionForMimetype(const QString &mimetype)
The base class on which all widgets and screens are based.
Definition: mythuitype.h:63
bool IsInputToggled(void)
returns true if all keypresses are to be passed to the web page
void loadFinished(bool ok)
a page is starting to load
const char * kgetType
Key event handler.
static Type kEventType
Definition: mythdialogbox.h:57
void SetMaximum(int value)
void slotTopScreenChanged(MythScreenType *screen)
bool FindThemeFile(QString &path)
void loadStarted(void)
QString GetDefaultSaveDirectory(void)
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
static void Play(void)
QString GetTitle(void)
Gets the current page's title.
QString GetConfDir(void)
Definition: mythdirs.cpp:224
QNetworkReply * createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData=nullptr) override
void Hide(void)
virtual void Close()
bool IsActive() const
double GetFloatSetting(const QString &key, double defaultval=0.0)
void slotLoadFinished(bool Ok)
void SetHtml(const QString &html, const QUrl &baseUrl=QUrl())
Sets the content of the widget to the specified html.
void closeBusyPopup(void)
BrowserApi * m_api
MythUIScrollBar * m_horizontalScrollbar
QString GetMetadata(void)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
This class is used as a container for messages.
Definition: mythevent.h:16
void Back(void)
Got backward in page history.
~BrowserApi(void) override
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
void slotTitleChanged(const QString &title)
bool Create(void) override
void loadProgress(int progress)
a page has finished loading
void handleUnsupportedContent(QNetworkReply *reply)
virtual MythScreenType * GetTopScreen(void) const
void Refresh(void)
void slotScrollBarShowing(void)
MythWebView * m_browser
static int SupportedMimeTypesCount
void openBusyPopup(const QString &message)
Subclass of QWebView.
void customEvent(QEvent *e) override
void showDownloadMenu(void)
int GetStackCount(void)
static QString getFirstText(QDomElement &element)
void refreshCookieJar(QNetworkCookieJar *jar)
Refresh the temporary cookie jar from another cookie jar.
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
MythUIWebBrowser(MythUIType *parent, const QString &name)
the classes constructor
bool CanGoBack(void)
Can we go backward in page history.
virtual void Pulse(void)
Pulse is called 70 times a second to trigger a single frame of an animation.
Definition: mythuitype.cpp:440
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
void titleChanged(const QString &title)
% of page loaded
void slotScrollBarHiding(void)
~MythUIWebBrowser() override
the classes destructor
void doDownload(const QString &saveFilename)
void slotLoadStarted(void)
a file has been downloaded
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
void SetDefaultSaveDirectory(const QString &saveDir)
static MythUIType * GetGlobalObjectStore(void)
MythRect m_Area
Definition: mythuitype.h:249
bool IsOnTopScreen(void)
is our containing screen the top screen?
QWebView * createWindow(QWebPage::WebWindowType type) override
QString m_extension
float GetZoom(void)
Get the current zoom level.
int GetVolume(void)
static void PlayURL(const QString &url)
MythUIHelper * GetMythUI()
MythWebPage * m_webpage
void slotLoadProgress(int progress)
void dispatch(const MythEvent &event)
MythMainWindow * GetMythMainWindow(void)
void Showing()
bool CanGoForward(void)
Can go forward in page history.
void SetActive(bool active)
Toggles the active state of the widget.
QElapsedTimer m_lastUpdateTime
static void PlayTrack(int trackID)
int GetNumSetting(const QString &key, int defaultval=0)
Dialog prompting the user to enter a text string.
void SetZoom(float zoom)
Set the text size to specific size.
void SetSliderPosition(int value)
void SetBackgroundColor(QColor color)
Sets the default background color.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static bool isMusicFile(const QString &extension, const QString &mimetype)
QNetworkRequest m_downloadRequest
void setWebView(QWebView *view)
MythUIBusyDialog * m_busyPopup
MythWebPage(QObject *parent=nullptr)
bool GetBoolSetting(const QString &key, bool defaultval=false)
static void PlayFile(const QString &filename)
QIcon GetIcon(void)
Gets the current page's fav icon.
QNetworkReply * m_downloadReply
QVariant evaluateJavaScript(const QString &scriptSource)
Evaluates the JavaScript code in scriptSource.
void DrawSelf(MythPainter *p, int xoffset, int yoffset, int alphaMod, QRect clipRect) override
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
static void SetVolume(int volumn)
void statusBarMessage(const QString &text)
a pages title has changed
bool extension(Extension extension, const ExtensionOption *option=nullptr, ExtensionReturn *output=nullptr) override
void ZoomIn(void)
Increase the text size.
static MythRect parseRect(const QString &text, bool normalize=true)
bool supportsExtension(Extension extension) const override
static void Pause(void)
void Finalize(void) override
Perform any post-xml parsing initialisation tasks.
QString userAgentForUrl(const QUrl &url) const override
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="", int lenMins=120, const QString &year="1895", const QString &id="", bool useBookmarks=false)
Screen in which all other widgets are contained and rendered.
void Assign(const QImage &img)
Definition: mythimage.cpp:80
Web browsing widget.
QString m_answer
void LoadUserStyleSheet(const QUrl &url)
Sets the specified user style sheet.
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
void Hiding()
QString GetHostName(void)
void saveCookieJar(const QString &filename)
Saves the cookie jar to a cookie file.
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
void doDownloadRequested(const QNetworkRequest &request)
static bool parseBool(const QString &text)
void SetCanTakeFocus(bool set=true)
Set whether this widget can take focus.
Definition: mythuitype.cpp:342
QString getReplyMimetype(void)
void loadCookieJar(const QString &filename)
Loads the cookie jar from a cookie file.
void IncrementCurrentTime(void)
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
BrowserApi(QObject *parent)
~MythWebView(void) override
QString m_defaultSaveFilename
static MythNetworkAccessManager * networkManager
MythScreenType * m_parentScreen
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
QString GetDefaultSaveFilename(void)
QWebFrame * m_frame
#define output