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 mimeType;
43  QString extension;
44  bool 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  QTime timer;
185  timer.start();
186 
187  while (timer.elapsed() < 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  QTime timer;
229  timer.start();
230 
231  while (timer.elapsed() < 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  MythEvent *me = static_cast<MythEvent *>(e);
248  const QString& message = me->Message();
249 
250  if (!message.startsWith("MUSIC_CONTROL"))
251  return;
252 
253  QStringList tokens = message.simplified().split(" ");
254 
255  if ((tokens.size() >= 4) && (tokens[1] == "ANSWER")
256  && (tokens[2] == gCoreContext->GetHostName()))
257  {
258  m_answer = tokens[3];
259 
260  for (int i = 4; i < tokens.size(); i++)
261  m_answer += QString(" ") + tokens[i];
262 
263  m_gotAnswer = true;
264  }
265  }
266 }
267 
268 MythWebPage::MythWebPage(QObject *parent)
269  : QWebPage(parent)
270 {
271  setNetworkAccessManager(GetNetworkAccessManager());
272 }
273 
275 {
276  LOG(VB_GENERAL, LOG_DEBUG, "Refreshing DLManager's Cookie Jar");
278  GetMythDownloadManager()->saveCookieJar(GetConfDir() + "/MythBrowser/cookiejar.txt");
279 }
280 
281 bool MythWebPage::supportsExtension(Extension extension) const
282 {
283  return extension == QWebPage::ErrorPageExtension;
284 }
285 
286 bool MythWebPage::extension(Extension extension, const ExtensionOption *option,
287  ExtensionReturn *output)
288 {
289  if (extension == QWebPage::ErrorPageExtension)
290  {
291  if (!option || !output)
292  return false;
293 
294  const ErrorPageExtensionOption *erroroption
295  = static_cast<const ErrorPageExtensionOption *>(option);
296  ErrorPageExtensionReturn *erroroutput = nullptr;
297  erroroutput = static_cast<ErrorPageExtensionReturn *>(output);
298 
299  QString filename = "htmls/notfound.html";
300 
301  if (!GetMythUI()->FindThemeFile(filename))
302  return false;
303 
304  QFile file(QLatin1String(qPrintable(filename)));
305  bool isOpened = file.open(QIODevice::ReadOnly);
306 
307  if (!isOpened)
308  return false;
309 
310  QString title = tr("Error loading page: %1").arg(erroroption->url.toString());
311  QString html = QString(QLatin1String(file.readAll()))
312  .arg(title)
313  .arg(erroroption->errorString)
314  .arg(erroroption->url.toString());
315 
316  QBuffer imageBuffer;
317  imageBuffer.open(QBuffer::ReadWrite);
318  QIcon icon = qApp->style()->standardIcon(QStyle::SP_MessageBoxWarning,
319  nullptr, nullptr);
320  QPixmap pixmap = icon.pixmap(QSize(32, 32));
321 
322  if (pixmap.save(&imageBuffer, "PNG"))
323  {
324  html.replace(QLatin1String("IMAGE_BINARY_DATA_HERE"),
325  QString(QLatin1String(imageBuffer.buffer().toBase64())));
326  }
327 
328  erroroutput->content = html.toUtf8();
329 
330  return true;
331  }
332 
333  return false;
334 }
335 
336 QString MythWebPage::userAgentForUrl(const QUrl &url) const
337 {
338  return QWebPage::userAgentForUrl(url).replace("Safari", "MythBrowser");
339 }
340 
346 MythWebView::MythWebView(QWidget *parent, MythUIWebBrowser *parentBrowser)
347  : QWebView(parent),
348  m_webpage(new MythWebPage(this))
349 {
350  setPage(m_webpage);
351 
352  m_parentBrowser = parentBrowser;
353 
354  connect(page(), SIGNAL(unsupportedContent(QNetworkReply *)),
355  this, SLOT(handleUnsupportedContent(QNetworkReply *)));
356 
357  connect(page(), SIGNAL(downloadRequested(const QNetworkRequest &)),
358  this, SLOT(handleDownloadRequested(QNetworkRequest)));
359 
360  page()->setForwardUnsupportedContent(true);
361 
362  m_api = new BrowserApi(this);
363  m_api->setWebView(this);
364 }
365 
367 {
368  delete m_webpage;
369  delete m_api;
370 }
371 
376 const char *kgetType = "\
377 function activeElement()\
378 {\
379  var type;\
380  type = document.activeElement.type;\
381  return type;\
382 }\
383 activeElement();";
384 
385 void MythWebView::keyPressEvent(QKeyEvent *event)
386 {
387  // does an edit have focus?
388  QString type = m_parentBrowser->evaluateJavaScript(QString(kgetType))
389  .toString().toLower();
390  bool editHasFocus = (type == "text" || type == "textarea" ||
391  type == "password");
392 
393  // if the QWebView widget has focus then all keypresses from a regular
394  // keyboard get sent here first
395  if (editHasFocus || m_parentBrowser->IsInputToggled())
396  {
397  // input is toggled so pass all keypresses to the QWebView's handler
398  QWebView::keyPressEvent(event);
399  }
400  else
401  {
402  // we need to convert a few keypress events so the QWebView does the
403  // right thing
404  QStringList actions;
405  bool handled = false;
406  handled = GetMythMainWindow()->TranslateKeyPress("Browser", event,
407  actions);
408 
409  for (int i = 0; i < actions.size() && !handled; i++)
410  {
411  QString action = actions[i];
412  handled = true;
413 
414  if (action == "NEXTLINK")
415  {
416  QKeyEvent tabKey(event->type(), Qt::Key_Tab,
417  event->modifiers(), QString(),
418  event->isAutoRepeat(), event->count());
419  *event = tabKey;
420  QWebView::keyPressEvent(event);
421  return;
422  }
423  if (action == "PREVIOUSLINK")
424  {
425  QKeyEvent shiftTabKey(event->type(), Qt::Key_Tab,
426  event->modifiers() | Qt::ShiftModifier,
427  QString(),
428  event->isAutoRepeat(), event->count());
429  *event = shiftTabKey;
430  QWebView::keyPressEvent(event);
431  return;
432  }
433  if (action == "FOLLOWLINK")
434  {
435  QKeyEvent returnKey(event->type(), Qt::Key_Return,
436  event->modifiers(), QString(),
437  event->isAutoRepeat(), event->count());
438  *event = returnKey;
439  QWebView::keyPressEvent(event);
440  return;
441  }
442  }
443 
444  // pass the keyPress event to our main window handler so they get
445  // handled properly by the various mythui handlers
446  QCoreApplication::postEvent(GetMythMainWindow(), new QKeyEvent(*event));
447  }
448 }
449 
450 void MythWebView::handleUnsupportedContent(QNetworkReply *reply)
451 {
452  if (reply->error() == QNetworkReply::NoError)
453  {
454  stop();
455 
456  QVariant header = reply->header(QNetworkRequest::ContentTypeHeader);
457 
458  if (header != QVariant())
459  LOG(VB_GENERAL, LOG_ERR,
460  QString("MythWebView::handleUnsupportedContent - %1")
461  .arg(header.toString()));
462 
463  m_downloadReply = reply;
464  m_downloadRequest = reply->request();
465  m_downloadAndPlay = false;
467 
468  return;
469  }
470 }
471 
472 void MythWebView::handleDownloadRequested(const QNetworkRequest &request)
473 {
474  m_downloadReply = nullptr;
475  doDownloadRequested(request);
476 }
477 
478 void MythWebView::doDownloadRequested(const QNetworkRequest &request)
479 {
480  m_downloadRequest = request;
481 
482  // get the filename from the url if available
483  QFileInfo fi(request.url().path());
484  QString basename(fi.completeBaseName());
485  QString extension = fi.suffix().toLower();
486  QString mimetype = getReplyMimetype();
487 
488  // if we have a default filename use that
489  QString saveBaseName = basename;
490 
491  if (!m_parentBrowser->GetDefaultSaveFilename().isEmpty())
492  {
493  QFileInfo savefi(m_parentBrowser->GetDefaultSaveFilename());
494  saveBaseName = savefi.completeBaseName();
495  }
496 
497  // if the filename is still empty use a default name
498  if (saveBaseName.isEmpty())
499  saveBaseName = "unnamed_download";
500 
501  // if we don't have an extension from the filename get one from the mime type
502  if (extension.isEmpty())
503  extension = getExtensionForMimetype(mimetype);
504 
505  if (!extension.isEmpty())
506  extension = '.' + extension;
507 
508  QString saveFilename = QString("%1%2%3")
510  .arg(saveBaseName)
511  .arg(extension);
512 
513  // dont overwrite an existing file
514  if (QFile::exists(saveFilename))
515  {
516  int i = 1;
517 
518  do
519  {
520  saveFilename = QString("%1%2-%3%4")
522  .arg(saveBaseName)
523  .arg(QString::number(i++))
524  .arg(extension);
525  }
526  while (QFile::exists(saveFilename));
527  }
528 
529  // if we are downloading and then playing the file don't ask for the file name
530  if (m_downloadAndPlay)
531  {
532  doDownload(saveFilename);
533  }
534  else
535  {
536  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
537 
538  QString msg = tr("Enter filename to save file");
539  MythTextInputDialog *input = new MythTextInputDialog(popupStack, msg,
540  FilterNone, false,
541  saveFilename);
542 
543  if (input->Create())
544  {
545  input->SetReturnEvent(this, "filenamedialog");
546  popupStack->AddScreen(input);
547  }
548  else
549  delete input;
550  }
551 }
552 
553 void MythWebView::doDownload(const QString &saveFilename)
554 {
555  if (saveFilename.isEmpty())
556  return;
557 
558  openBusyPopup(tr("Downloading file. Please wait..."));
559 
560  // No need to make sure the path to saveFilename exists because
561  // MythDownloadManage takes care of that
563  saveFilename, this);
564 }
565 
566 void MythWebView::openBusyPopup(const QString &message)
567 {
568  if (m_busyPopup)
569  return;
570 
571  QString msg(tr("Downloading..."));
572 
573  if (!message.isEmpty())
574  msg = message;
575 
576  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
577  m_busyPopup = new MythUIBusyDialog(msg, popupStack, "downloadbusydialog");
578 
579  if (m_busyPopup->Create())
580  popupStack->AddScreen(m_busyPopup, false);
581 }
582 
584 {
585  if (m_busyPopup)
586  m_busyPopup->Close();
587 
588  m_busyPopup = nullptr;
589 }
590 
591 void MythWebView::customEvent(QEvent *event)
592 {
593  if (event->type() == DialogCompletionEvent::kEventType)
594  {
596 
597  // make sure the user didn't ESCAPE out of the dialog
598  if (dce->GetResult() < 0)
599  return;
600 
601  QString resultid = dce->GetId();
602  QString resulttext = dce->GetResultText();
603 
604  if (resultid == "filenamedialog")
605  doDownload(resulttext);
606  else if (resultid == "downloadmenu")
607  {
608  if (resulttext == tr("Play the file"))
609  {
610  QFileInfo fi(m_downloadRequest.url().path());
611  QString basename(fi.baseName());
612  QString extension = fi.suffix();
613  QString mimeType = getReplyMimetype();
614 
615  if (isMusicFile(extension, mimeType))
616  {
617  MythEvent me(QString("MUSIC_COMMAND %1 PLAY_URL %2")
618  .arg(gCoreContext->GetHostName())
619  .arg(m_downloadRequest.url().toString()));
620  gCoreContext->dispatch(me);
621  }
622  else if (isVideoFile(extension, mimeType))
623  GetMythMainWindow()->HandleMedia("Internal",
624  m_downloadRequest.url().toString());
625  else
626  LOG(VB_GENERAL, LOG_ERR,
627  QString("MythWebView: Asked to play a file with "
628  "extension '%1' but don't know how")
629  .arg(extension));
630  }
631  else if (resulttext == tr("Download the file"))
632  {
634  }
635  else if (resulttext == tr("Download and play the file"))
636  {
637  m_downloadAndPlay = true;
639  }
640  }
641  }
642  else if (event->type() == MythEvent::MythEventMessage)
643  {
644  MythEvent *me = static_cast<MythEvent *>(event);
645  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
646 
647  if (tokens.isEmpty())
648  return;
649 
650  if (tokens[0] == "DOWNLOAD_FILE")
651  {
652  QStringList args = me->ExtraDataList();
653 
654  if (tokens[1] == "UPDATE")
655  {
656  // could update a progressbar here
657  }
658  else if (tokens[1] == "FINISHED")
659  {
660  int fileSize = args[2].toInt();
661  int errorCode = args[4].toInt();
662  QString filename = args[1];
663 
664  closeBusyPopup();
665 
666  if ((errorCode != 0) || (fileSize == 0))
667  ShowOkPopup(tr("ERROR downloading file."));
668  else if (m_downloadAndPlay)
669  GetMythMainWindow()->HandleMedia("Internal", filename);
670 
671  MythEvent me2(QString("BROWSER_DOWNLOAD_FINISHED"), args);
672  gCoreContext->dispatch(me2);
673  }
674  }
675  }
676 }
677 
679 {
680  QFileInfo fi(m_downloadRequest.url().path());
681  QString basename(fi.baseName());
682  QString extension = fi.suffix();
683  QString mimeType = getReplyMimetype();
684 
685  QString label = tr("What do you want to do with this file?");
686 
687  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
688 
689  MythDialogBox *menu = new MythDialogBox(label, popupStack, "downloadmenu");
690 
691  if (!menu->Create())
692  {
693  delete menu;
694  return;
695  }
696 
697  menu->SetReturnEvent(this, "downloadmenu");
698 
699  if (isMusicFile(extension, mimeType))
700  menu->AddButton(tr("Play the file"));
701 
702  if (isVideoFile(extension, mimeType))
703  menu->AddButton(tr("Download and play the file"));
704 
705  menu->AddButton(tr("Download the file"));
706  menu->AddButton(tr("Cancel"));
707 
708  popupStack->AddScreen(menu);
709 }
710 
711 QString MythWebView::getExtensionForMimetype(const QString &mimetype)
712 {
713  for (int x = 0; x < SupportedMimeTypesCount; x++)
714  {
715  if (!mimetype.isEmpty() && mimetype == SupportedMimeTypes[x].mimeType)
716  return SupportedMimeTypes[x].extension;
717  }
718 
719  return QString("");
720 }
721 
722 bool MythWebView::isMusicFile(const QString &extension, const QString &mimetype)
723 {
724  for (int x = 0; x < SupportedMimeTypesCount; x++)
725  {
726  if (!SupportedMimeTypes[x].isVideo)
727  {
728  if (!mimetype.isEmpty() &&
729  mimetype == SupportedMimeTypes[x].mimeType)
730  return true;
731 
732  if (!extension.isEmpty() &&
733  extension.toLower() == SupportedMimeTypes[x].extension)
734  return true;
735  }
736  }
737 
738  return false;
739 }
740 
741 bool MythWebView::isVideoFile(const QString &extension, const QString &mimetype)
742 {
743  for (int x = 0; x < SupportedMimeTypesCount; x++)
744  {
746  {
747  if (!mimetype.isEmpty() &&
748  mimetype == SupportedMimeTypes[x].mimeType)
749  return true;
750 
751  if (!extension.isEmpty() &&
752  extension.toLower() == SupportedMimeTypes[x].extension)
753  return true;
754  }
755  }
756 
757  return false;
758 }
759 
761 {
762  if (!m_downloadReply)
763  return QString();
764 
765  QString mimeType;
766  QVariant header = m_downloadReply->header(QNetworkRequest::ContentTypeHeader);
767 
768  if (header != QVariant())
769  mimeType = header.toString();
770 
771  return mimeType;
772 }
773 
774 QWebView *MythWebView::createWindow(QWebPage::WebWindowType /* type */)
775 {
776  return (QWebView *) this;
777 }
778 
779 
820  : MythUIType(parent, name),
821  m_parentScreen(nullptr),
822  m_browser(nullptr), m_browserArea(MythRect()),
823  m_actualBrowserArea(MythRect()), m_image(nullptr),
824  m_active(false), m_wasActive(false),
825  m_initialized(false), m_lastUpdateTime(QTime()),
826  m_updateInterval(0), m_zoom(1.0),
827  m_bgColor("White"), m_widgetUrl(QUrl()), m_userCssFile(""),
828  m_defaultSaveDir(GetConfDir() + "/MythBrowser/"),
829  m_defaultSaveFilename(""),
830  m_inputToggled(false), m_lastMouseAction(""),
831  m_mouseKeyCount(0),
832  m_horizontalScrollbar(nullptr), m_verticalScrollbar(nullptr)
833 {
834  SetCanTakeFocus(true);
835  m_scrollAnimation.setDuration(0);
836 }
837 
842 {
844 
845  Init();
846 }
847 
856 {
857  if (m_initialized)
858  return;
859 
862  m_actualBrowserArea.translate(m_Area.x(), m_Area.y());
863 
864  if (!m_actualBrowserArea.isValid())
866 
867  m_browser = new MythWebView(GetMythMainWindow()->GetPaintWindow(), this);
868  m_browser->setPalette(qApp->style()->standardPalette());
869  m_browser->setGeometry(m_actualBrowserArea);
870  m_browser->setFixedSize(m_actualBrowserArea.size());
872  m_browser->page()->setLinkDelegationPolicy(QWebPage::DontDelegateLinks);
873 
874  bool err = false;
875  UIUtilW::Assign(this, m_horizontalScrollbar, "horizscrollbar", &err);
876  UIUtilW::Assign(this, m_verticalScrollbar, "vertscrollbar", &err);
878  {
879  QWebFrame* frame = m_browser->page()->currentFrame();
880  frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
881  connect(m_horizontalScrollbar, SIGNAL(Hiding()),
882  this, SLOT(slotScrollBarHiding()));
883  connect(m_horizontalScrollbar, SIGNAL(Showing()),
884  this, SLOT(slotScrollBarShowing()));
885  }
886 
888  {
889  QWebFrame* frame = m_browser->page()->currentFrame();
890  frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
891  connect(m_verticalScrollbar, SIGNAL(Hiding()),
892  this, SLOT(slotScrollBarHiding()));
893  connect(m_verticalScrollbar, SIGNAL(Showing()),
894  this, SLOT(slotScrollBarShowing()));
895  }
896 
897  // if we have a valid css URL use that ...
898  if (!m_userCssFile.isEmpty())
899  {
900  QString filename = m_userCssFile;
901 
902  if (GetMythUI()->FindThemeFile(filename))
903  LoadUserStyleSheet(QUrl("file://" + filename));
904  }
905  else
906  {
907  // ...otherwise use the default one
908  QString filename = "htmls/mythbrowser.css";
909 
910  if (GetMythUI()->FindThemeFile(filename))
911  LoadUserStyleSheet(QUrl("file://" + filename));
912  }
913 
914  m_browser->winId();
915 
917 
918  connect(m_browser, SIGNAL(loadStarted()),
919  this, SLOT(slotLoadStarted()));
920  connect(m_browser, SIGNAL(loadFinished(bool)),
921  this, SLOT(slotLoadFinished(bool)));
922  connect(m_browser, SIGNAL(loadProgress(int)),
923  this, SLOT(slotLoadProgress(int)));
924  connect(m_browser, SIGNAL(titleChanged(const QString &)),
925  this, SLOT(slotTitleChanged(const QString &)));
926  connect(m_browser, SIGNAL(iconChanged(void)),
927  this, SLOT(slotIconChanged(void)));
928  connect(m_browser, SIGNAL(statusBarMessage(const QString &)),
929  this, SLOT(slotStatusBarMessage(const QString &)));
930  connect(m_browser->page(), SIGNAL(linkHovered(const QString &,
931  const QString &,
932  const QString &)),
933  this, SLOT(slotStatusBarMessage(const QString &)));
934  connect(m_browser, SIGNAL(linkClicked(const QUrl &)),
935  this, SLOT(slotLinkClicked(const QUrl &)));
936 
937  // find what screen we are on
938  m_parentScreen = nullptr;
939  QObject *parentObject = parent();
940 
941  while (parentObject)
942  {
943  m_parentScreen = dynamic_cast<MythScreenType *>(parentObject);
944 
945  if (m_parentScreen)
946  break;
947 
948  parentObject = parentObject->parent();
949  }
950 
951  if (!m_parentScreen && parent() != GetGlobalObjectStore())
952  LOG(VB_GENERAL, LOG_ERR,
953  "MythUIWebBrowser: failed to find our parent screen");
954 
955  // connect to the topScreenChanged signals on each screen stack
956  for (int x = 0; x < GetMythMainWindow()->GetStackCount(); x++)
957  {
959 
960  if (stack)
961  connect(stack, SIGNAL(topScreenChanged(MythScreenType *)),
962  this, SLOT(slotTopScreenChanged(MythScreenType *)));
963  }
964 
965  // set up the icon cache directory
966  QString path = GetConfDir();
967  QDir dir(path);
968 
969  if (!dir.exists())
970  dir.mkdir(path);
971 
972  path += "/MythBrowser";
973  dir.setPath(path);
974 
975  if (!dir.exists())
976  dir.mkdir(path);
977 
978  QWebSettings::setIconDatabasePath(path);
979 
980  if (gCoreContext->GetNumSetting("WebBrowserEnablePlugins", 1) == 1)
981  {
982  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: enabling plugins");
983  QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
984  true);
985  }
986  else
987  {
988  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling plugins");
989  QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled,
990  false);
991  }
992 
993  if (!gCoreContext->GetBoolSetting("WebBrowserEnableJavascript",true))
994  {
995  LOG(VB_GENERAL, LOG_INFO, "MythUIWebBrowser: disabling JavaScript");
996  QWebSettings::globalSettings()->setAttribute(QWebSettings::JavascriptEnabled, false);
997  }
998 
999  QImage image = QImage(m_actualBrowserArea.size(), QImage::Format_ARGB32);
1001  m_image->Assign(image);
1002 
1004 
1005  m_zoom = gCoreContext->GetFloatSetting("WebBrowserZoomLevel", 1.0);
1006 
1007  SetZoom(m_zoom);
1008 
1009  if (!m_widgetUrl.isEmpty() && m_widgetUrl.isValid())
1011 
1012  m_initialized = true;
1013 }
1014 
1019 {
1020  if (m_browser)
1021  {
1022  m_browser->hide();
1023  m_browser->disconnect();
1024  m_browser->deleteLater();
1025  m_browser = nullptr;
1026  }
1027 
1028  if (m_image)
1029  {
1030  m_image->DecrRef();
1031  m_image = nullptr;
1032  }
1033 }
1034 
1039 void MythUIWebBrowser::LoadPage(const QUrl& url)
1040 {
1041  if (!m_browser)
1042  return;
1043 
1044  ResetScrollBars();
1045 
1046  m_browser->setUrl(url);
1047 }
1048 
1055 void MythUIWebBrowser::SetHtml(const QString &html, const QUrl &baseUrl)
1056 {
1057  if (!m_browser)
1058  return;
1059 
1060  ResetScrollBars();
1061 
1062  m_browser->setHtml(html, baseUrl);
1063 }
1064 
1070 {
1071  if (!m_browser)
1072  return;
1073 
1074  LOG(VB_GENERAL, LOG_INFO,
1075  "MythUIWebBrowser: Loading css from - " + url.toString());
1076 
1077  m_browser->page()->settings()->setUserStyleSheetUrl(url);
1078 }
1079 
1087 {
1088  if (!m_browser)
1089  return;
1090 
1091  color.setAlpha(255);
1092  QPalette palette = m_browser->page()->palette();
1093  palette.setBrush(QPalette::Window, QBrush(color));
1094  palette.setBrush(QPalette::Base, QBrush(color));
1095  m_browser->page()->setPalette(palette);
1096 
1097  UpdateBuffer();
1098 }
1099 
1111 {
1112  if (m_active == active)
1113  return;
1114 
1115  m_active = active;
1116  m_wasActive = active;
1117 
1118  if (m_active)
1119  {
1120  m_browser->setUpdatesEnabled(false);
1121  m_browser->setFocus();
1122  m_browser->show();
1123  m_browser->raise();
1124  if (qApp->platformName().contains("egl"))
1125  {
1126  m_browser->setParent(nullptr);
1127  m_browser->setFocus();
1128  m_browser->show();
1129  m_browser->raise();
1130  }
1131  m_browser->setUpdatesEnabled(true);
1132  }
1133  else
1134  {
1135  m_browser->clearFocus();
1136  m_browser->hide();
1137  if (qApp->platformName().contains("egl"))
1138  m_browser->setParent(GetMythMainWindow());
1139  UpdateBuffer();
1140  }
1141 }
1142 
1147 {
1148  SetZoom(m_zoom + 0.1F);
1149 }
1150 
1155 {
1156  SetZoom(m_zoom - 0.1F);
1157 }
1158 
1164 {
1165  if (!m_browser)
1166  return;
1167 
1168  if (zoom < 0.3F)
1169  zoom = 0.3F;
1170 
1171  if (zoom > 5.0F)
1172  zoom = 5.0F;
1173 
1174  m_zoom = zoom;
1175  m_browser->setZoomFactor(m_zoom);
1176  ResetScrollBars();
1177  UpdateBuffer();
1178 
1179  slotStatusBarMessage(tr("Zoom: %1%").arg(m_zoom * 100));
1180 
1181  gCoreContext->SaveSetting("WebBrowserZoomLevel", QString("%1").arg(m_zoom));
1182 }
1183 
1184 void MythUIWebBrowser::SetDefaultSaveDirectory(const QString &saveDir)
1185 {
1186  if (!saveDir.isEmpty())
1187  m_defaultSaveDir = saveDir;
1188  else
1189  m_defaultSaveDir = GetConfDir() + "/MythBrowser/";
1190 }
1191 
1192 void MythUIWebBrowser::SetDefaultSaveFilename(const QString &filename)
1193 {
1194  if (!filename.isEmpty())
1195  m_defaultSaveFilename = filename;
1196  else
1197  m_defaultSaveFilename.clear();
1198 }
1199 
1205 {
1206  return m_zoom;
1207 }
1208 
1215 {
1216  if (!m_browser)
1217  return false;
1218 
1219  return m_browser->history()->canGoForward();
1220 }
1221 
1228 {
1229  if (!m_browser)
1230  return false;
1231 
1232  return m_browser->history()->canGoBack();
1233 }
1234 
1239 {
1240  if (!m_browser)
1241  return;
1242 
1243  m_browser->back();
1244 }
1245 
1250 {
1251  if (!m_browser)
1252  return;
1253 
1254  m_browser->forward();
1255 }
1256 
1262 {
1263  if (m_browser)
1264  {
1265  return QWebSettings::iconForUrl(m_browser->url());
1266  }
1267  return QIcon();
1268 }
1269 
1275 {
1276  if (m_browser)
1277  {
1278  return m_browser->url();
1279  }
1280  return QUrl();
1281 }
1282 
1288 {
1289  if (m_browser)
1290  return m_browser->title();
1291  return QString("");
1292 }
1293 
1298 QVariant MythUIWebBrowser::evaluateJavaScript(const QString &scriptSource)
1299 {
1300  if (m_browser)
1301  {
1302  QWebFrame *frame = m_browser->page()->currentFrame();
1303  return frame->evaluateJavaScript(scriptSource);
1304  }
1305  return QVariant();
1306 }
1307 
1308 void MythUIWebBrowser::Scroll(int dx, int dy)
1309 {
1310  QPoint startPos = m_browser->page()->currentFrame()->scrollPosition();
1311  QPoint endPos = startPos + QPoint(dx, dy);
1312 
1313  if (GetPainter()->SupportsAnimation() && m_scrollAnimation.duration() > 0)
1314  {
1315  // Previous scroll has been completed
1316  if (m_destinationScrollPos == startPos)
1317  m_scrollAnimation.setEasingCurve(QEasingCurve::InOutCubic);
1318  else
1319  m_scrollAnimation.setEasingCurve(QEasingCurve::OutCubic);
1320 
1321  m_destinationScrollPos = endPos;
1322  m_scrollAnimation.setStartValue(startPos);
1325  }
1326  else
1327  {
1328  m_destinationScrollPos = endPos;
1329  m_browser->page()->currentFrame()->setScrollPosition(endPos);
1330  UpdateBuffer();
1331  }
1332 }
1333 
1335 {
1336  ResetScrollBars();
1337  emit loadStarted();
1338 }
1339 
1341 {
1342  UpdateBuffer();
1343  emit loadFinished(ok);
1344 }
1345 
1347 {
1348  emit loadProgress(progress);
1349 }
1350 
1351 void MythUIWebBrowser::slotTitleChanged(const QString &title)
1352 {
1353  emit titleChanged(title);
1354 }
1355 
1357 {
1358  emit statusBarMessage(text);
1359 }
1360 
1362 {
1363  LoadPage(url);
1364 }
1365 
1367 {
1368  emit iconChanged();
1369 }
1370 
1372 {
1373  bool wasActive = (m_wasActive || m_active);
1374  SetActive(false);
1375  m_wasActive = wasActive;
1376 }
1377 
1379 {
1381  slotTopScreenChanged(nullptr);
1382 }
1383 
1385 {
1386  if (IsOnTopScreen())
1388  else
1389  {
1390  bool wasActive = (m_wasActive || m_active);
1391  SetActive(false);
1392  m_wasActive = wasActive;
1393  }
1394 }
1395 
1398 {
1399  if (!m_parentScreen)
1400  return false;
1401 
1402  for (int x = GetMythMainWindow()->GetStackCount() - 1; x >= 0; x--)
1403  {
1405 
1406  // ignore stacks with no screens on them
1407  if (!stack->GetTopScreen())
1408  continue;
1409 
1410  return (stack->GetTopScreen() == m_parentScreen);
1411  }
1412 
1413  return false;
1414 }
1415 
1416 
1418 {
1419  QPoint position = m_browser->page()->currentFrame()->scrollPosition();
1420  if (m_verticalScrollbar)
1421  {
1422  int maximum =
1423  m_browser->page()->currentFrame()->contentsSize().height() -
1424  m_actualBrowserArea.height();
1425  m_verticalScrollbar->SetMaximum(maximum);
1427  m_verticalScrollbar->SetSliderPosition(position.y());
1428  }
1429 
1431  {
1432  int maximum =
1433  m_browser->page()->currentFrame()->contentsSize().width() -
1434  m_actualBrowserArea.width();
1438  }
1439 }
1440 
1442 {
1443  UpdateScrollBars();
1444 
1445  if (!m_image)
1446  return;
1447 
1448  if (!m_active || (m_active && !m_browser->hasFocus()))
1449  {
1450  QPainter painter(m_image);
1451  m_browser->render(&painter);
1452  painter.end();
1453 
1454  m_image->SetChanged();
1455  Refresh();
1456  }
1457 }
1458 
1463 {
1464  if (m_scrollAnimation.IsActive() &&
1466  m_browser->page()->currentFrame()->scrollPosition())
1467  {
1469 
1470  QPoint scrollPosition = m_scrollAnimation.currentValue().toPoint();
1471  m_browser->page()->currentFrame()->setScrollPosition(scrollPosition);
1472 
1473  SetRedraw();
1474  UpdateBuffer();
1475  }
1476  else if (m_updateInterval && m_lastUpdateTime.elapsed() > m_updateInterval)
1477  {
1478  UpdateBuffer();
1479  m_lastUpdateTime.start();
1480  }
1481 
1483 }
1484 
1488 void MythUIWebBrowser::DrawSelf(MythPainter *p, int xoffset, int yoffset,
1489  int alphaMod, QRect clipRect)
1490 {
1491  if (!m_image || m_image->isNull() || !m_browser || m_browser->hasFocus())
1492  return;
1493 
1494  QRect area = m_actualBrowserArea;
1495  area.translate(xoffset, yoffset);
1496 
1497  p->SetClipRect(clipRect);
1498  p->DrawImage(area.x(), area.y(), m_image, alphaMod);
1499 }
1500 
1504 bool MythUIWebBrowser::keyPressEvent(QKeyEvent *event)
1505 {
1506  QStringList actions;
1507  bool handled = false;
1508  handled = GetMythMainWindow()->TranslateKeyPress("Browser", event, actions);
1509 
1510  for (int i = 0; i < actions.size() && !handled; i++)
1511  {
1512  QString action = actions[i];
1513  handled = true;
1514 
1515  if (action == "TOGGLEINPUT")
1516  {
1518 
1519  if (m_inputToggled)
1520  slotStatusBarMessage(tr("Sending key presses to web page"));
1521  else
1522  slotStatusBarMessage(tr("Sending key presses to MythTV"));
1523 
1524  return true;
1525  }
1526 
1527  // if input is toggled all input goes to the web page
1528  if (m_inputToggled)
1529  {
1530  m_browser->keyPressEvent(event);
1531 
1532  return true;
1533  }
1534 
1535  QWebFrame *frame = m_browser->page()->currentFrame();
1536  if (action == "UP")
1537  {
1538  int pos = frame->scrollPosition().y();
1539 
1540  if (pos > 0)
1541  {
1542  Scroll(0, -m_actualBrowserArea.height() / 10);
1543  }
1544  else
1545  handled = false;
1546  }
1547  else if (action == "DOWN")
1548  {
1549  int pos = frame->scrollPosition().y();
1550  QSize maximum = frame->contentsSize() - m_actualBrowserArea.size();
1551 
1552  if (pos != maximum.height())
1553  {
1554  Scroll(0, m_actualBrowserArea.height() / 10);
1555  }
1556  else
1557  handled = false;
1558  }
1559  else if (action == "LEFT")
1560  {
1561  int pos = frame->scrollPosition().x();
1562 
1563  if (pos > 0)
1564  {
1565  Scroll(-m_actualBrowserArea.width() / 10, 0);
1566  }
1567  else
1568  handled = false;
1569  }
1570  else if (action == "RIGHT")
1571  {
1572  int pos = frame->scrollPosition().x();
1573  QSize maximum = frame->contentsSize() - m_actualBrowserArea.size();
1574 
1575  if (pos != maximum.width())
1576  {
1577  Scroll(m_actualBrowserArea.width() / 10, 0);
1578  }
1579  else
1580  handled = false;
1581  }
1582  else if (action == "PAGEUP")
1583  {
1584  Scroll(0, -m_actualBrowserArea.height());
1585  }
1586  else if (action == "PAGEDOWN")
1587  {
1588  Scroll(0, m_actualBrowserArea.height());
1589  }
1590  else if (action == "ZOOMIN")
1591  {
1592  ZoomIn();
1593  }
1594  else if (action == "ZOOMOUT")
1595  {
1596  ZoomOut();
1597  }
1598  else if (action == "MOUSEUP" || action == "MOUSEDOWN" ||
1599  action == "MOUSELEFT" || action == "MOUSERIGHT" ||
1600  action == "MOUSELEFTBUTTON")
1601  {
1603  }
1604  else if (action == "PAGELEFT")
1605  {
1606  Scroll(-m_actualBrowserArea.width(), 0);
1607  }
1608  else if (action == "PAGERIGHT")
1609  {
1610  Scroll(m_actualBrowserArea.width(), 0);
1611  }
1612  else if (action == "NEXTLINK")
1613  {
1614  m_browser->keyPressEvent(event);
1615  }
1616  else if (action == "PREVIOUSLINK")
1617  {
1618  m_browser->keyPressEvent(event);
1619  }
1620  else if (action == "FOLLOWLINK")
1621  {
1622  m_browser->keyPressEvent(event);
1623  }
1624  else if (action == "HISTORYBACK")
1625  {
1626  Back();
1627  }
1628  else if (action == "HISTORYFORWARD")
1629  {
1630  Forward();
1631  }
1632  else
1633  handled = false;
1634  }
1635 
1636  return handled;
1637 }
1638 
1640 {
1641  int step = 5;
1642 
1643  // speed up mouse movement if the same key is held down
1644  if (action == m_lastMouseAction &&
1645  m_lastMouseActionTime.msecsTo(QTime::currentTime()) < 500)
1646  {
1647  m_lastMouseActionTime = QTime::currentTime();
1648  m_mouseKeyCount++;
1649 
1650  if (m_mouseKeyCount > 5)
1651  step = 25;
1652  }
1653  else
1654  {
1656  m_lastMouseActionTime = QTime::currentTime();
1657  m_mouseKeyCount = 1;
1658  }
1659 
1660  if (action == "MOUSEUP")
1661  {
1662  QPoint curPos = QCursor::pos();
1663  QCursor::setPos(curPos.x(), curPos.y() - step);
1664  }
1665  else if (action == "MOUSELEFT")
1666  {
1667  QPoint curPos = QCursor::pos();
1668  QCursor::setPos(curPos.x() - step, curPos.y());
1669  }
1670  else if (action == "MOUSERIGHT")
1671  {
1672  QPoint curPos = QCursor::pos();
1673  QCursor::setPos(curPos.x() + step, curPos.y());
1674  }
1675  else if (action == "MOUSEDOWN")
1676  {
1677  QPoint curPos = QCursor::pos();
1678  QCursor::setPos(curPos.x(), curPos.y() + step);
1679  }
1680  else if (action == "MOUSELEFTBUTTON")
1681  {
1682  QPoint curPos = QCursor::pos();
1683  QWidget *widget = QApplication::widgetAt(curPos);
1684 
1685  if (widget)
1686  {
1687  curPos = widget->mapFromGlobal(curPos);
1688 
1689  QMouseEvent *me = new QMouseEvent(QEvent::MouseButtonPress, curPos,
1690  Qt::LeftButton, Qt::LeftButton,
1691  Qt::NoModifier);
1692  QCoreApplication::postEvent(widget, me);
1693 
1694  me = new QMouseEvent(QEvent::MouseButtonRelease, curPos,
1695  Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1696  QCoreApplication::postEvent(widget, me);
1697  }
1698  }
1699 }
1700 
1702 {
1703  if (m_verticalScrollbar)
1704  {
1707  }
1708 
1710  {
1713  }
1714 }
1715 
1720  const QString &filename, QDomElement &element, bool showWarnings)
1721 {
1722  if (element.tagName() == "zoom")
1723  {
1724  QString zoom = getFirstText(element);
1725  m_zoom = zoom.toFloat();
1726  }
1727  else if (element.tagName() == "url")
1728  {
1729  m_widgetUrl.setUrl(getFirstText(element));
1730  }
1731  else if (element.tagName() == "userstylesheet")
1732  {
1733  m_userCssFile = getFirstText(element);
1734  }
1735  else if (element.tagName() == "updateinterval")
1736  {
1737  QString interval = getFirstText(element);
1738  m_updateInterval = interval.toInt();
1739  }
1740  else if (element.tagName() == "background")
1741  {
1742  m_bgColor = QColor(element.attribute("color", "#ffffff"));
1743  int alpha = element.attribute("alpha", "255").toInt();
1744  m_bgColor.setAlpha(alpha);
1745  }
1746  else if (element.tagName() == "browserarea")
1747  {
1748  m_browserArea = parseRect(element);
1749  }
1750  else if (element.tagName() == "scrollduration")
1751  {
1752  QString duration = getFirstText(element);
1753  m_scrollAnimation.setDuration(duration.toInt());
1754  }
1755  else if (element.tagName() == "acceptsfocus")
1756  {
1757  SetCanTakeFocus(parseBool(element));
1758  }
1759  else
1760  {
1761  return MythUIType::ParseElement(filename, element, showWarnings);
1762  }
1763 
1764  return true;
1765 }
1766 
1771 {
1772  MythUIWebBrowser *browser = dynamic_cast<MythUIWebBrowser *>(base);
1773 
1774  if (!browser)
1775  {
1776  LOG(VB_GENERAL, LOG_ERR, "ERROR, bad parsing");
1777  return;
1778  }
1779 
1780  MythUIType::CopyFrom(base);
1781 
1782  m_browserArea = browser->m_browserArea;
1783  m_zoom = browser->m_zoom;
1784  m_bgColor = browser->m_bgColor;
1785  m_widgetUrl = browser->m_widgetUrl;
1786  m_userCssFile = browser->m_userCssFile;
1790  m_scrollAnimation.setDuration(browser->m_scrollAnimation.duration());
1791 }
1792 
1797 {
1798  MythUIWebBrowser *browser = new MythUIWebBrowser(parent, objectName());
1799  browser->CopyFrom(this);
1800 }
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)
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.
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 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 queueDownload(const QString &url, const QString &dest, QObject *caller, const bool reload=false)
Adds a url to the download queue.
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:45
void SaveSetting(const QString &key, int newValue)
static Type MythEventMessage
Definition: mythevent.h:66
void keyPressEvent(QKeyEvent *event) override
void SetRedraw(void)
Definition: mythuitype.cpp:295
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[]
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)
void Stop(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
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
static bool isVideo(const QString &mimeType)
Definition: newssite.cpp:293
void Init(void)
Initializes the widget ready for use.
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:50
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.
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
bool Create(void) override
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.
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 SetReturnEvent(QObject *retobject, const QString &resultid)
void showDownloadMenu(void)
int GetStackCount(void)
~MythUIWebBrowser()
the classes destructor
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:442
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)
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
float GetZoom(void)
Get the current zoom level.
const char * name
Definition: ParseText.cpp:328
int GetVolume(void)
void PlayURL(const QString &url)
MythUIHelper * GetMythUI()
QString extension
MythWebPage * m_webpage
void slotLoadProgress(int progress)
void dispatch(const MythEvent &event)
MythMainWindow * GetMythMainWindow(void)
virtual void DrawImage(const QRect &dest, MythImage *im, const QRect &src, int alpha)=0
void Showing()
bool CanGoForward(void)
Can go forward in page history.
void SetActive(bool active)
Toggles the active state of the widget.
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 SetBackgroundColor(QColor color)
Sets the default background color.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
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)
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.
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
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)
QString mimeType
Screen in which all other widgets are contained and rendered.
void Assign(const QImage &img)
Definition: mythimage.cpp:80
Web browsing widget.
const QString & Message() const
Definition: mythevent.h:58
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:37
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:344
virtual void SetClipRect(const QRect &clipRect)
Definition: mythpainter.cpp:48
QString getReplyMimetype(void)
const QStringList & ExtraDataList() const
Definition: mythevent.h:60
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)
QString m_defaultSaveFilename
static MythNetworkAccessManager * networkManager
MythScreenType * m_parentScreen
void SetSliderPosition(int)
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