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  if (m_initialized)
876  return;
877 
880  m_actualBrowserArea.translate(m_Area.x(), m_Area.y());
881 
882  if (!m_actualBrowserArea.isValid())
884 
885  m_browser = new MythWebView(GetMythMainWindow()->GetPaintWindow(), this);
886  m_browser->setPalette(qApp->style()->standardPalette());
887  m_browser->setGeometry(m_actualBrowserArea);
888  m_browser->setFixedSize(m_actualBrowserArea.size());
890  m_browser->page()->setLinkDelegationPolicy(QWebPage::DontDelegateLinks);
891 
892  bool err = false;
893  UIUtilW::Assign(this, m_horizontalScrollbar, "horizscrollbar", &err);
894  UIUtilW::Assign(this, m_verticalScrollbar, "vertscrollbar", &err);
896  {
897  QWebFrame* frame = m_browser->page()->currentFrame();
898  frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
899  connect(m_horizontalScrollbar, SIGNAL(Hiding()),
900  this, SLOT(slotScrollBarHiding()));
901  connect(m_horizontalScrollbar, SIGNAL(Showing()),
902  this, SLOT(slotScrollBarShowing()));
903  }
904 
906  {
907  QWebFrame* frame = m_browser->page()->currentFrame();
908  frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
909  connect(m_verticalScrollbar, SIGNAL(Hiding()),
910  this, SLOT(slotScrollBarHiding()));
911  connect(m_verticalScrollbar, SIGNAL(Showing()),
912  this, SLOT(slotScrollBarShowing()));
913  }
914 
915  // if we have a valid css URL use that ...
916  if (!m_userCssFile.isEmpty())
917  {
918  QString filename = m_userCssFile;
919 
921  LoadUserStyleSheet(QUrl("file://" + filename));
922  }
923  else
924  {
925  // ...otherwise use the default one
926  QString filename = "htmls/mythbrowser.css";
927 
929  LoadUserStyleSheet(QUrl("file://" + filename));
930  }
931 
932  m_browser->winId();
933 
935 
936  connect(m_browser, SIGNAL(loadStarted()),
937  this, SLOT(slotLoadStarted()));
938  connect(m_browser, SIGNAL(loadFinished(bool)),
939  this, SLOT(slotLoadFinished(bool)));
940  connect(m_browser, SIGNAL(loadProgress(int)),
941  this, SLOT(slotLoadProgress(int)));
942  connect(m_browser, SIGNAL(titleChanged(const QString &)),
943  this, SLOT(slotTitleChanged(const QString &)));
944  connect(m_browser, SIGNAL(iconChanged(void)),
945  this, SLOT(slotIconChanged(void)));
946  connect(m_browser, SIGNAL(statusBarMessage(const QString &)),
947  this, SLOT(slotStatusBarMessage(const QString &)));
948  connect(m_browser->page(), SIGNAL(linkHovered(const QString &,
949  const QString &,
950  const QString &)),
951  this, SLOT(slotStatusBarMessage(const QString &)));
952  connect(m_browser, SIGNAL(linkClicked(const QUrl &)),
953  this, SLOT(slotLinkClicked(const QUrl &)));
954 
955  // find what screen we are on
956  m_parentScreen = nullptr;
957  QObject *parentObject = parent();
958 
959  while (parentObject)
960  {
961  m_parentScreen = dynamic_cast<MythScreenType *>(parentObject);
962 
963  if (m_parentScreen)
964  break;
965 
966  parentObject = parentObject->parent();
967  }
968 
969  if (!m_parentScreen && parent() != GetGlobalObjectStore())
970  LOG(VB_GENERAL, LOG_ERR,
971  "MythUIWebBrowser: failed to find our parent screen");
972 
973  // connect to the topScreenChanged signals on each screen stack
974  for (int x = 0; x < GetMythMainWindow()->GetStackCount(); x++)
975  {
977 
978  if (stack)
979  connect(stack, SIGNAL(topScreenChanged(MythScreenType *)),
980  this, SLOT(slotTopScreenChanged(MythScreenType *)));
981  }
982 
983  // set up the icon cache directory
984  QString path = GetConfDir();
985  QDir dir(path);
986 
987  if (!dir.exists())
988  dir.mkdir(path);
989 
990  path += "/MythBrowser";
991  dir.setPath(path);
992 
993  if (!dir.exists())
994  dir.mkdir(path);
995 
996  QWebSettings::setIconDatabasePath(path);
997 
998  if (gCoreContext->GetNumSetting("WebBrowserEnablePlugins", 1) == 1)
999  {
1000  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: enabling plugins");
1001  QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
1002  true);
1003  }
1004  else
1005  {
1006  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling plugins");
1007  QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
1008  false);
1009  }
1010 
1011  if (!gCoreContext->GetBoolSetting("WebBrowserEnableJavascript",true))
1012  {
1013  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling JavaScript");
1014  QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled, false);
1015  }
1016 
1017  QImage image = QImage(m_actualBrowserArea.size(), QImage::Format_ARGB32);
1019  m_image->Assign(image);
1020 
1022 
1023  m_zoom = gCoreContext->GetFloatSetting("WebBrowserZoomLevel", 1.0);
1024 
1025  SetZoom(m_zoom);
1026 
1027  if (!m_widgetUrl.isEmpty() && m_widgetUrl.isValid())
1029 
1030  m_initialized = true;
1031 }
1032 
1037 {
1038  if (m_browser)
1039  {
1040  m_browser->hide();
1041  m_browser->disconnect();
1042  m_browser->deleteLater();
1043  m_browser = nullptr;
1044  }
1045 
1046  if (m_image)
1047  {
1048  m_image->DecrRef();
1049  m_image = nullptr;
1050  }
1051 }
1052 
1057 void MythUIWebBrowser::LoadPage(const QUrl& url)
1058 {
1059  if (!m_browser)
1060  return;
1061 
1062  ResetScrollBars();
1063 
1064  m_browser->setUrl(url);
1065 }
1066 
1073 void MythUIWebBrowser::SetHtml(const QString &html, const QUrl &baseUrl)
1074 {
1075  if (!m_browser)
1076  return;
1077 
1078  ResetScrollBars();
1079 
1080  m_browser->setHtml(html, baseUrl);
1081 }
1082 
1088 {
1089  if (!m_browser)
1090  return;
1091 
1092  LOG(VB_GENERAL, LOG_INFO,
1093  "MythUIWebBrowser: Loading css from - " + url.toString());
1094 
1095  m_browser->page()->settings()->setUserStyleSheetUrl(url);
1096 }
1097 
1105 {
1106  if (!m_browser)
1107  return;
1108 
1109  color.setAlpha(255);
1110  QPalette palette = m_browser->page()->palette();
1111  palette.setBrush(QPalette::Window, QBrush(color));
1112  palette.setBrush(QPalette::Base, QBrush(color));
1113  m_browser->page()->setPalette(palette);
1114 
1115  UpdateBuffer();
1116 }
1117 
1129 {
1130  if (m_active == active)
1131  return;
1132 
1133  m_active = active;
1134  m_wasActive = active;
1135 
1136  if (m_active)
1137  {
1138  m_browser->setUpdatesEnabled(false);
1139  m_browser->setFocus();
1140  m_browser->show();
1141  m_browser->raise();
1142  if (qApp->platformName().contains("egl"))
1143  {
1144  m_browser->setParent(nullptr);
1145  m_browser->setFocus();
1146  m_browser->show();
1147  m_browser->raise();
1148  }
1149  m_browser->setUpdatesEnabled(true);
1150  }
1151  else
1152  {
1153  m_browser->clearFocus();
1154  m_browser->hide();
1155  if (qApp->platformName().contains("egl"))
1156  m_browser->setParent(GetMythMainWindow());
1157  UpdateBuffer();
1158  }
1159 }
1160 
1165 {
1166  SetZoom(m_zoom + 0.1F);
1167 }
1168 
1173 {
1174  SetZoom(m_zoom - 0.1F);
1175 }
1176 
1182 {
1183  if (!m_browser)
1184  return;
1185 
1186  if (zoom < 0.3F)
1187  zoom = 0.3F;
1188 
1189  if (zoom > 5.0F)
1190  zoom = 5.0F;
1191 
1192  m_zoom = zoom;
1193  m_browser->setZoomFactor(m_zoom);
1194  ResetScrollBars();
1195  UpdateBuffer();
1196 
1197  slotStatusBarMessage(tr("Zoom: %1%").arg(m_zoom * 100));
1198 
1199  gCoreContext->SaveSetting("WebBrowserZoomLevel", QString("%1").arg(m_zoom));
1200 }
1201 
1202 void MythUIWebBrowser::SetDefaultSaveDirectory(const QString &saveDir)
1203 {
1204  if (!saveDir.isEmpty())
1205  m_defaultSaveDir = saveDir;
1206  else
1207  m_defaultSaveDir = GetConfDir() + "/MythBrowser/";
1208 }
1209 
1211 {
1212  if (!filename.isEmpty())
1214  else
1215  m_defaultSaveFilename.clear();
1216 }
1217 
1223 {
1224  return m_zoom;
1225 }
1226 
1233 {
1234  if (!m_browser)
1235  return false;
1236 
1237  return m_browser->history()->canGoForward();
1238 }
1239 
1246 {
1247  if (!m_browser)
1248  return false;
1249 
1250  return m_browser->history()->canGoBack();
1251 }
1252 
1257 {
1258  if (!m_browser)
1259  return;
1260 
1261  m_browser->back();
1262 }
1263 
1268 {
1269  if (!m_browser)
1270  return;
1271 
1272  m_browser->forward();
1273 }
1274 
1280 {
1281  if (m_browser)
1282  {
1283  return QWebSettings::iconForUrl(m_browser->url());
1284  }
1285  return QIcon();
1286 }
1287 
1293 {
1294  if (m_browser)
1295  {
1296  return m_browser->url();
1297  }
1298  return QUrl();
1299 }
1300 
1306 {
1307  if (m_browser)
1308  return m_browser->title();
1309  return QString("");
1310 }
1311 
1316 QVariant MythUIWebBrowser::evaluateJavaScript(const QString &scriptSource)
1317 {
1318  if (m_browser)
1319  {
1320  QWebFrame *frame = m_browser->page()->currentFrame();
1321  return frame->evaluateJavaScript(scriptSource);
1322  }
1323  return QVariant();
1324 }
1325 
1326 void MythUIWebBrowser::Scroll(int dx, int dy)
1327 {
1328  QPoint startPos = m_browser->page()->currentFrame()->scrollPosition();
1329  QPoint endPos = startPos + QPoint(dx, dy);
1330 
1331  if (GetPainter()->SupportsAnimation() && m_scrollAnimation.duration() > 0)
1332  {
1333  // Previous scroll has been completed
1334  if (m_destinationScrollPos == startPos)
1335  m_scrollAnimation.setEasingCurve(QEasingCurve::InOutCubic);
1336  else
1337  m_scrollAnimation.setEasingCurve(QEasingCurve::OutCubic);
1338 
1339  m_destinationScrollPos = endPos;
1340  m_scrollAnimation.setStartValue(startPos);
1343  }
1344  else
1345  {
1346  m_destinationScrollPos = endPos;
1347  m_browser->page()->currentFrame()->setScrollPosition(endPos);
1348  UpdateBuffer();
1349  }
1350 }
1351 
1353 {
1354  ResetScrollBars();
1355  emit loadStarted();
1356 }
1357 
1359 {
1360  UpdateBuffer();
1361  emit loadFinished(ok);
1362 }
1363 
1365 {
1366  emit loadProgress(progress);
1367 }
1368 
1369 void MythUIWebBrowser::slotTitleChanged(const QString &title)
1370 {
1371  emit titleChanged(title);
1372 }
1373 
1375 {
1376  emit statusBarMessage(text);
1377 }
1378 
1380 {
1381  LoadPage(url);
1382 }
1383 
1385 {
1386  emit iconChanged();
1387 }
1388 
1390 {
1391  bool wasActive = (m_wasActive || m_active);
1392  SetActive(false);
1393  m_wasActive = wasActive;
1394 }
1395 
1397 {
1399  slotTopScreenChanged(nullptr);
1400 }
1401 
1403 {
1404  if (IsOnTopScreen())
1406  else
1407  {
1408  bool wasActive = (m_wasActive || m_active);
1409  SetActive(false);
1410  m_wasActive = wasActive;
1411  }
1412 }
1413 
1416 {
1417  if (!m_parentScreen)
1418  return false;
1419 
1420  for (int x = GetMythMainWindow()->GetStackCount() - 1; x >= 0; x--)
1421  {
1423 
1424  // ignore stacks with no screens on them
1425  if (!stack->GetTopScreen())
1426  continue;
1427 
1428  return (stack->GetTopScreen() == m_parentScreen);
1429  }
1430 
1431  return false;
1432 }
1433 
1434 
1436 {
1437  QPoint position = m_browser->page()->currentFrame()->scrollPosition();
1438  if (m_verticalScrollbar)
1439  {
1440  int maximum =
1441  m_browser->page()->currentFrame()->contentsSize().height() -
1442  m_actualBrowserArea.height();
1443  m_verticalScrollbar->SetMaximum(maximum);
1445  m_verticalScrollbar->SetSliderPosition(position.y());
1446  }
1447 
1449  {
1450  int maximum =
1451  m_browser->page()->currentFrame()->contentsSize().width() -
1452  m_actualBrowserArea.width();
1456  }
1457 }
1458 
1460 {
1461  UpdateScrollBars();
1462 
1463  if (!m_image)
1464  return;
1465 
1466  if (!m_active || (m_active && !m_browser->hasFocus()))
1467  {
1468  QPainter painter(m_image);
1469  m_browser->render(&painter);
1470  painter.end();
1471 
1472  m_image->SetChanged();
1473  Refresh();
1474  }
1475 }
1476 
1481 {
1482  if (m_scrollAnimation.IsActive() &&
1484  m_browser->page()->currentFrame()->scrollPosition())
1485  {
1487 
1488  QPoint scrollPosition = m_scrollAnimation.currentValue().toPoint();
1489  m_browser->page()->currentFrame()->setScrollPosition(scrollPosition);
1490 
1491  SetRedraw();
1492  UpdateBuffer();
1493  }
1494  else if (m_updateInterval && m_lastUpdateTime.hasExpired(m_updateInterval))
1495  {
1496  UpdateBuffer();
1497  m_lastUpdateTime.start();
1498  }
1499 
1501 }
1502 
1506 void MythUIWebBrowser::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1507  int alphaMod, QRect clipRect)
1508 {
1509  if (!m_image || m_image->isNull() || !m_browser || m_browser->hasFocus())
1510  return;
1511 
1512  QRect area = m_actualBrowserArea;
1513  area.translate(xoffset, yoffset);
1514 
1515  p->SetClipRect(clipRect);
1516  p->DrawImage(area.x(), area.y(), m_image, alphaMod);
1517 }
1518 
1522 bool MythUIWebBrowser::keyPressEvent(QKeyEvent *event)
1523 {
1524  QStringList actions;
1525  bool handled = false;
1526  handled = GetMythMainWindow()->TranslateKeyPress("Browser", event, actions);
1527 
1528  for (int i = 0; i < actions.size() && !handled; i++)
1529  {
1530  QString action = actions[i];
1531  handled = true;
1532 
1533  if (action == "TOGGLEINPUT")
1534  {
1536 
1537  if (m_inputToggled)
1538  slotStatusBarMessage(tr("Sending key presses to web page"));
1539  else
1540  slotStatusBarMessage(tr("Sending key presses to MythTV"));
1541 
1542  return true;
1543  }
1544 
1545  // if input is toggled all input goes to the web page
1546  if (m_inputToggled)
1547  {
1548  m_browser->keyPressEvent(event);
1549 
1550  return true;
1551  }
1552 
1553  QWebFrame *frame = m_browser->page()->currentFrame();
1554  if (action == "UP")
1555  {
1556  int pos = frame->scrollPosition().y();
1557 
1558  if (pos > 0)
1559  {
1560  Scroll(0, -m_actualBrowserArea.height() / 10);
1561  }
1562  else
1563  handled = false;
1564  }
1565  else if (action == "DOWN")
1566  {
1567  int pos = frame->scrollPosition().y();
1568  QSize maximum = frame->contentsSize() - m_actualBrowserArea.size();
1569 
1570  if (pos != maximum.height())
1571  {
1572  Scroll(0, m_actualBrowserArea.height() / 10);
1573  }
1574  else
1575  handled = false;
1576  }
1577  else if (action == "LEFT")
1578  {
1579  int pos = frame->scrollPosition().x();
1580 
1581  if (pos > 0)
1582  {
1583  Scroll(-m_actualBrowserArea.width() / 10, 0);
1584  }
1585  else
1586  handled = false;
1587  }
1588  else if (action == "RIGHT")
1589  {
1590  int pos = frame->scrollPosition().x();
1591  QSize maximum = frame->contentsSize() - m_actualBrowserArea.size();
1592 
1593  if (pos != maximum.width())
1594  {
1595  Scroll(m_actualBrowserArea.width() / 10, 0);
1596  }
1597  else
1598  handled = false;
1599  }
1600  else if (action == "PAGEUP")
1601  {
1602  Scroll(0, -m_actualBrowserArea.height());
1603  }
1604  else if (action == "PAGEDOWN")
1605  {
1606  Scroll(0, m_actualBrowserArea.height());
1607  }
1608  else if (action == "ZOOMIN")
1609  {
1610  ZoomIn();
1611  }
1612  else if (action == "ZOOMOUT")
1613  {
1614  ZoomOut();
1615  }
1616  else if (action == "MOUSEUP" || action == "MOUSEDOWN" ||
1617  action == "MOUSELEFT" || action == "MOUSERIGHT" ||
1618  action == "MOUSELEFTBUTTON")
1619  {
1621  }
1622  else if (action == "PAGELEFT")
1623  {
1624  Scroll(-m_actualBrowserArea.width(), 0);
1625  }
1626  else if (action == "PAGERIGHT")
1627  {
1628  Scroll(m_actualBrowserArea.width(), 0);
1629  }
1630  else if ((action == "NEXTLINK") ||
1631  (action == "PREVIOUSLINK") ||
1632  (action == "FOLLOWLINK"))
1633  {
1634  m_browser->keyPressEvent(event);
1635  }
1636  else if (action == "HISTORYBACK")
1637  {
1638  Back();
1639  }
1640  else if (action == "HISTORYFORWARD")
1641  {
1642  Forward();
1643  }
1644  else
1645  handled = false;
1646  }
1647 
1648  return handled;
1649 }
1650 
1652 {
1653  int step = 5;
1654 
1655  // speed up mouse movement if the same key is held down
1656  if (action == m_lastMouseAction &&
1657  m_lastMouseActionTime.isValid() &&
1658  !m_lastMouseActionTime.hasExpired(500))
1659  {
1660  m_lastMouseActionTime.start();
1661  m_mouseKeyCount++;
1662 
1663  if (m_mouseKeyCount > 5)
1664  step = 25;
1665  }
1666  else
1667  {
1669  m_lastMouseActionTime.start();
1670  m_mouseKeyCount = 1;
1671  }
1672 
1673  if (action == "MOUSEUP")
1674  {
1675  QPoint curPos = QCursor::pos();
1676  QCursor::setPos(curPos.x(), curPos.y() - step);
1677  }
1678  else if (action == "MOUSELEFT")
1679  {
1680  QPoint curPos = QCursor::pos();
1681  QCursor::setPos(curPos.x() - step, curPos.y());
1682  }
1683  else if (action == "MOUSERIGHT")
1684  {
1685  QPoint curPos = QCursor::pos();
1686  QCursor::setPos(curPos.x() + step, curPos.y());
1687  }
1688  else if (action == "MOUSEDOWN")
1689  {
1690  QPoint curPos = QCursor::pos();
1691  QCursor::setPos(curPos.x(), curPos.y() + step);
1692  }
1693  else if (action == "MOUSELEFTBUTTON")
1694  {
1695  QPoint curPos = QCursor::pos();
1696  QWidget *widget = QApplication::widgetAt(curPos);
1697 
1698  if (widget)
1699  {
1700  curPos = widget->mapFromGlobal(curPos);
1701 
1702  auto *me = new QMouseEvent(QEvent::MouseButtonPress, curPos,
1703  Qt::LeftButton, Qt::LeftButton,
1704  Qt::NoModifier);
1705  QCoreApplication::postEvent(widget, me);
1706 
1707  me = new QMouseEvent(QEvent::MouseButtonRelease, curPos,
1708  Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1709  QCoreApplication::postEvent(widget, me);
1710  }
1711  }
1712 }
1713 
1715 {
1716  if (m_verticalScrollbar)
1717  {
1720  }
1721 
1723  {
1726  }
1727 }
1728 
1733  const QString &filename, QDomElement &element, bool showWarnings)
1734 {
1735  if (element.tagName() == "zoom")
1736  {
1737  QString zoom = getFirstText(element);
1738  m_zoom = zoom.toFloat();
1739  }
1740  else if (element.tagName() == "url")
1741  {
1742  m_widgetUrl.setUrl(getFirstText(element));
1743  }
1744  else if (element.tagName() == "userstylesheet")
1745  {
1746  m_userCssFile = getFirstText(element);
1747  }
1748  else if (element.tagName() == "updateinterval")
1749  {
1750  QString interval = getFirstText(element);
1751  m_updateInterval = interval.toInt();
1752  }
1753  else if (element.tagName() == "background")
1754  {
1755  m_bgColor = QColor(element.attribute("color", "#ffffff"));
1756  int alpha = element.attribute("alpha", "255").toInt();
1757  m_bgColor.setAlpha(alpha);
1758  }
1759  else if (element.tagName() == "browserarea")
1760  {
1761  m_browserArea = parseRect(element);
1762  }
1763  else if (element.tagName() == "scrollduration")
1764  {
1765  QString duration = getFirstText(element);
1766  m_scrollAnimation.setDuration(duration.toInt());
1767  }
1768  else if (element.tagName() == "acceptsfocus")
1769  {
1770  SetCanTakeFocus(parseBool(element));
1771  }
1772  else
1773  {
1774  return MythUIType::ParseElement(filename, element, showWarnings);
1775  }
1776 
1777  return true;
1778 }
1779 
1784 {
1785  auto *browser = dynamic_cast<MythUIWebBrowser *>(base);
1786  if (!browser)
1787  {
1788  LOG(VB_GENERAL, LOG_ERR, "ERROR, bad parsing");
1789  return;
1790  }
1791 
1792  MythUIType::CopyFrom(base);
1793 
1794  m_browserArea = browser->m_browserArea;
1795  m_zoom = browser->m_zoom;
1796  m_bgColor = browser->m_bgColor;
1797  m_widgetUrl = browser->m_widgetUrl;
1798  m_userCssFile = browser->m_userCssFile;
1799  m_updateInterval = browser->m_updateInterval;
1800  m_defaultSaveDir = browser->m_defaultSaveDir;
1801  m_defaultSaveFilename = browser->m_defaultSaveFilename;
1802  m_scrollAnimation.setDuration(browser->m_scrollAnimation.duration());
1803 }
1804 
1809 {
1810  auto *browser = new MythUIWebBrowser(parent, objectName());
1811  browser->CopyFrom(this);
1812 }
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:49
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:56
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