MythTV  master
recordingselector.cpp
Go to the documentation of this file.
1 
2 // c++
3 #include <cstdlib>
4 #include <unistd.h>
5 
6 // qt
7 #include <QDir>
8 #include <QKeyEvent>
9 #include <QTimer>
10 #include <QApplication>
11 
12 // mythtv
13 #include <mythcontext.h>
14 #include <mythdb.h>
15 #include <mthread.h>
16 #include <programinfo.h>
17 #include <remoteutil.h>
18 #include <mythtimer.h>
19 #include <mythuitext.h>
20 #include <mythuibutton.h>
21 #include <mythuiimage.h>
22 #include <mythuibuttonlist.h>
23 #include <mythmainwindow.h>
24 #include <mythprogressdialog.h>
25 #include <mythdialogbox.h>
26 #include <mythlogging.h>
27 #include <mythdate.h>
28 
29 // mytharchive
30 #include "recordingselector.h"
31 #include "archiveutil.h"
32 
34 {
35  public:
37  MThread("GetRecordingList"), m_parent(parent)
38  {
39  start();
40  }
41 
42  void run(void) override // MThread
43  {
44  RunProlog();
46  RunEpilog();
47  }
48 
50 };
51 
53 {
54  delete m_recordingList;
55  while (!m_selectedList.isEmpty())
56  delete m_selectedList.takeFirst();
57 }
58 
60 {
61  // Load the theme for this screen
62  bool foundtheme = LoadWindowFromXML("mytharchive-ui.xml", "recording_selector", this);
63  if (!foundtheme)
64  return false;
65 
66  bool err = false;
67  UIUtilE::Assign(this, m_okButton, "ok_button", &err);
68  UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
69  UIUtilE::Assign(this, m_categorySelector, "category_selector", &err);
70  UIUtilE::Assign(this, m_recordingButtonList, "recordinglist", &err);
71 
72  UIUtilW::Assign(this, m_titleText, "progtitle", &err);
73  UIUtilW::Assign(this, m_datetimeText, "progdatetime", &err);
74  UIUtilW::Assign(this, m_descriptionText, "progdescription", &err);
75  UIUtilW::Assign(this, m_filesizeText, "filesize", &err);
76  UIUtilW::Assign(this, m_previewImage, "preview_image", &err);
77  UIUtilW::Assign(this, m_cutlistImage, "cutlist_image", &err);
78 
79  if (err)
80  {
81  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'recording_selector'");
82  return false;
83  }
84 
85  connect(m_okButton, SIGNAL(Clicked()), this, SLOT(OKPressed()));
86  connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(cancelPressed()));
87 
88  new MythUIButtonListItem(m_categorySelector, tr("All Recordings"));
89  connect(m_categorySelector, SIGNAL(itemSelected(MythUIButtonListItem *)),
90  this, SLOT(setCategory(MythUIButtonListItem *)));
91 
92  connect(m_recordingButtonList, SIGNAL(itemSelected(MythUIButtonListItem *)),
93  this, SLOT(titleChanged(MythUIButtonListItem *)));
94  connect(m_recordingButtonList, SIGNAL(itemClicked(MythUIButtonListItem *)),
95  this, SLOT(toggleSelected(MythUIButtonListItem *)));
96 
97  if (m_cutlistImage)
99 
100  BuildFocusList();
101 
103 
104  return true;
105 }
106 
108 {
109  QString message = tr("Retrieving Recording List.\nPlease Wait...");
110 
111  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
112 
113  MythUIBusyDialog *busyPopup = new
114  MythUIBusyDialog(message, popupStack, "recordingselectorbusydialog");
115 
116  if (busyPopup->Create())
117  popupStack->AddScreen(busyPopup, false);
118  else
119  {
120  delete busyPopup;
121  busyPopup = nullptr;
122  }
123 
125  while (thread->isRunning())
126  {
127  qApp->processEvents();
128  usleep(2000);
129  }
130 
131  if (!m_recordingList || m_recordingList->empty())
132  {
133  ShowOkPopup(tr("Either you don't have any recordings or "
134  "no recordings are available locally!"));
135  if (busyPopup)
136  busyPopup->Close();
137 
138  Close();
139  return;
140  }
141 
145 
146  if (busyPopup)
147  busyPopup->Close();
148 }
149 
150 bool RecordingSelector::keyPressEvent(QKeyEvent *event)
151 {
152  if (GetFocusWidget()->keyPressEvent(event))
153  return true;
154 
155  QStringList actions;
156  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
157 
158  for (int i = 0; i < actions.size() && !handled; i++)
159  {
160  QString action = actions[i];
161  handled = true;
162 
163  if (action == "MENU")
164  {
165  ShowMenu();
166  }
167  else
168  handled = false;
169  }
170 
171  if (!handled && MythScreenType::keyPressEvent(event))
172  handled = true;
173 
174  return handled;
175 }
176 
178 {
179  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
180 
181  MythDialogBox *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
182 
183  if (menuPopup->Create())
184  popupStack->AddScreen(menuPopup);
185 
186  menuPopup->SetReturnEvent(this, "action");
187 
188  menuPopup->AddButton(tr("Clear All"), SLOT(clearAll()));
189  menuPopup->AddButton(tr("Select All"), SLOT(selectAll()));
190 }
191 
193 {
194  while (!m_selectedList.isEmpty())
195  m_selectedList.takeFirst();
196  m_selectedList.clear();
197 
198  vector<ProgramInfo *>::iterator i = m_recordingList->begin();
199  for ( ; i != m_recordingList->end(); ++i)
200  {
201  ProgramInfo *p = *i;
202  m_selectedList.append(p);
203  }
204 
206 }
207 
209 {
210  while (!m_selectedList.isEmpty())
211  m_selectedList.takeFirst();
212  m_selectedList.clear();
213 
215 }
216 
218 {
220  {
221  int index = m_selectedList.indexOf(item->GetData().value<ProgramInfo *>());
222  if (index != -1)
223  m_selectedList.takeAt(index);
225  }
226  else
227  {
228  int index = m_selectedList.indexOf(item->GetData().value<ProgramInfo *>());
229  if (index == -1)
230  m_selectedList.append(item->GetData().value<ProgramInfo *>());
231 
233  }
234 }
235 
237 {
238  ProgramInfo *p = item->GetData().value<ProgramInfo *>();
239 
240  if (!p)
241  return;
242 
243  if (m_titleText)
245 
246  if (m_datetimeText)
247  m_datetimeText->SetText(p->GetScheduledStartTime().toLocalTime()
248  .toString("dd MMM yy (hh:mm)"));
249 
250  if (m_descriptionText)
251  {
253  ((!p->GetSubtitle().isEmpty()) ? p->GetSubtitle() + "\n" : "") +
254  p->GetDescription());
255  }
256 
257  if (m_filesizeText)
258  {
260  }
261 
262  if (m_cutlistImage)
263  {
264  if (p->HasCutlist())
265  m_cutlistImage->Show();
266  else
267  m_cutlistImage->Hide();
268  }
269 
270  if (m_previewImage)
271  {
272  // try to locate a preview image
273  if (QFile::exists(p->GetPathname() + ".png"))
274  {
275  m_previewImage->SetFilename(p->GetPathname() + ".png");
276  m_previewImage->Load();
277  }
278  else
279  {
280  m_previewImage->SetFilename("blank.png");
281  m_previewImage->Load();
282  }
283  }
284 }
285 
287 {
288  // loop though selected recordings and add them to the list
289  ProgramInfo *p;
290  ArchiveItem *a;
291 
292  // remove any items that have been removed from the list
293  QList<ArchiveItem *> tempAList;
294  for (int x = 0; x < m_archiveList->size(); x++)
295  {
296  a = m_archiveList->at(x);
297  bool found = false;
298 
299  for (int y = 0; y < m_selectedList.size(); y++)
300  {
301  p = m_selectedList.at(y);
302  if (a->type != "Recording" || a->filename == p->GetPlaybackURL(false, true))
303  {
304  found = true;
305  break;
306  }
307  }
308 
309  if (!found)
310  tempAList.append(a);
311  }
312 
313  for (int x = 0; x < tempAList.size(); x++)
314  m_archiveList->removeAll(tempAList.at(x));
315 
316  // remove any items that are already in the list
317  QList<ProgramInfo *> tempSList;
318  for (int x = 0; x < m_selectedList.size(); x++)
319  {
320  p = m_selectedList.at(x);
321 
322  for (int y = 0; y < m_archiveList->size(); y++)
323  {
324  a = m_archiveList->at(y);
325  if (a->filename == p->GetPlaybackURL(false, true))
326  {
327  tempSList.append(p);
328  break;
329  }
330  }
331  }
332 
333  for (int x = 0; x < tempSList.size(); x++)
334  m_selectedList.removeAll(tempSList.at(x));
335 
336  // add all that are left
337  for (int x = 0; x < m_selectedList.size(); x++)
338  {
339  p = m_selectedList.at(x);
340  a = new ArchiveItem;
341  a->type = "Recording";
342  a->title = p->GetTitle();
343  a->subtitle = p->GetSubtitle();
344  a->description = p->GetDescription();
346  .toLocalTime().toString("dd MMM yy");
348  .toLocalTime().toString("(hh:mm)");
349  a->size = p->GetFilesize();
350  a->filename = p->GetPlaybackURL(false, true);
351  a->hasCutlist = p->HasCutlist();
352  a->useCutlist = false;
353  a->duration = 0;
354  a->cutDuration = 0;
355  a->videoWidth = 0;
356  a->videoHeight = 0;
357  a->fileCodec = "";
358  a->videoCodec = "";
359  a->encoderProfile = nullptr;
360  a->editedDetails = false;
361  m_archiveList->append(a);
362  }
363 
364  emit haveResult(true);
365  Close();
366 }
367 
369 {
370  emit haveResult(false);
371  Close();
372 }
373 
375 {
376  if (!m_recordingList || m_recordingList->empty())
377  return;
378 
380 
381  if (m_categorySelector)
382  {
383  ProgramInfo *p;
384  vector<ProgramInfo *>::iterator i = m_recordingList->begin();
385  for ( ; i != m_recordingList->end(); ++i)
386  {
387  p = *i;
388 
389  if (p->GetTitle() == m_categorySelector->GetValue() ||
390  m_categorySelector->GetValue() == tr("All Recordings"))
391  {
394  p->GetTitle() + " ~ " +
395  p->GetScheduledStartTime().toLocalTime()
396  .toString("dd MMM yy (hh:mm)"));
397  item->setCheckable(true);
398  if (m_selectedList.indexOf(p) != -1)
399  {
401  }
402  else
403  {
405  }
406 
407  QString title = p->GetTitle();
408  QString subtitle = p->GetSubtitle();
409 
410  QDateTime recstartts = p->GetScheduledStartTime();
411  QDateTime recendts = p->GetScheduledEndTime();
412 
413  QString timedate = QString("%1 - %2")
415  .arg(MythDate::toString(recendts, MythDate::kTime));
416 
417  uint season = p->GetSeason();
418  uint episode = p->GetEpisode();
419  QString seasone, seasonx;
420 
421  if (season && episode)
422  {
423  seasone = QString("s%1e%2")
424  .arg(format_season_and_episode(season, 2))
425  .arg(format_season_and_episode(episode, 2));
426  seasonx = QString("%1x%2")
427  .arg(format_season_and_episode(season, 1))
428  .arg(format_season_and_episode(episode, 2));
429  }
430 
431  item->SetText(title, "title");
432  item->SetText(subtitle, "subtitle");
433  if (subtitle.isEmpty())
434  item->SetText(title, "titlesubtitle");
435  else
436  item->SetText(title + " - \"" + subtitle + '"',
437  "titlesubtitle");
438 
439  item->SetText(timedate, "timedate");
440  item->SetText(p->GetDescription(), "description");
441  item->SetText(formatSize(p->GetFilesize() / 1024),
442  "filesize_str");
443 
444  item->SetText(QString::number(season), "season");
445  item->SetText(QString::number(episode), "episode");
446  item->SetText(seasonx, "00x00");
447  item->SetText(seasone, "s00e00");
448 
449  item->DisplayState(p->HasCutlist() ? "yes" : "no", "cutlist");
450 
451  item->SetData(qVariantFromValue(p));
452  }
453  qApp->processEvents();
454  }
455  }
456 
459 }
460 
462 {
464  m_categories.clear();
465 
466  if (m_recordingList && !m_recordingList->empty())
467  {
468  vector<ProgramInfo *>::iterator i = m_recordingList->begin();
469  for ( ; i != m_recordingList->end(); ++i)
470  {
471  ProgramInfo *p = *i;
472  // ignore live tv and deleted recordings
473  if (p->GetRecordingGroup() == "LiveTV" ||
474  p->GetRecordingGroup() == "Deleted")
475  {
476  i = m_recordingList->erase(i);
477  --i;
478  continue;
479  }
480 
481  if (m_categories.indexOf(p->GetTitle()) == -1)
482  m_categories.append(p->GetTitle());
483  }
484  }
485 }
486 
488 {
489  // sort and add categories to selector
490  m_categories.sort();
491 
492  if (m_categorySelector)
493  {
494  new MythUIButtonListItem(m_categorySelector, tr("All Recordings"));
496 
497  for (int x = 0; x < m_categories.count(); x++)
498  {
500  }
501  }
502 }
503 
505 {
506  (void)item;
508 }
509 
511 {
512  if (!m_recordingList)
513  return;
514 
515  m_selectedList.clear();
516 
517  for (int x = 0; x < m_archiveList->size(); x++)
518  {
519  ArchiveItem *a = m_archiveList->at(x);
520  for (size_t y = 0; y < m_recordingList->size(); y++)
521  {
522  ProgramInfo *p = m_recordingList->at(y);
523  if (p->GetPlaybackURL(false, true) == a->filename)
524  {
525  if (m_selectedList.indexOf(p) == -1)
526  m_selectedList.append(p);
527  break;
528  }
529 
530  qApp->processEvents();
531  }
532  }
533 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
void Show(void)
void toggleSelected(MythUIButtonListItem *item)
RecordingSelector * m_parent
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
virtual uint64_t GetFilesize(void) const
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
void ShowMenu(void) override
void SetData(QVariant data)
bool hasCutlist
Definition: archiveutil.h:69
void haveResult(bool ok)
bool useCutlist
Definition: archiveutil.h:70
Basic menu dialog, message and a list of options.
QString GetTitle(void) const
Definition: programinfo.h:353
MPUBLIC QString format_season_and_episode(int seasEp, int digits)
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:136
MythScreenStack * GetStack(const QString &stackname)
unsigned int uint
Definition: compat.h:140
bool Create() override
int videoWidth
Definition: archiveutil.h:68
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void BuildFocusList(void)
MythUIImage * m_previewImage
MythUIButtonList * m_categorySelector
QString startTime
Definition: archiveutil.h:59
QString filename
Definition: archiveutil.h:60
QDateTime GetScheduledStartTime(void) const
The scheduled start time of program.
Definition: programinfo.h:382
void Hide(void)
virtual void Close()
void setCheckable(bool flag)
void AddButton(const QString &title, QVariant data=0, bool newMenu=false, bool setCurrent=false)
MythUIText * m_titleText
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
Holds information on recordings and videos.
Definition: programinfo.h:66
void titleChanged(MythUIButtonListItem *item)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
MythUIText * m_filesizeText
bool Create(void) override
Default local time.
Definition: mythdate.h:19
QString GetDescription(void) const
Definition: programinfo.h:357
void updateCategorySelector(void)
QString fileCodec
Definition: archiveutil.h:66
MythUIButtonList * m_recordingButtonList
bool HasCutlist(void) const
Definition: programinfo.h:471
bool editedDetails
Definition: archiveutil.h:71
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
bool isRunning(void) const
Definition: mthread.cpp:274
QString GetSubtitle(void) const
Definition: programinfo.h:355
int cutDuration
Definition: archiveutil.h:64
QString GetRecordingGroup(void) const
Definition: programinfo.h:411
friend class GetRecordingListThread
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
void updateRecordingList(void)
uint GetSeason(void) const
Definition: programinfo.h:358
std::vector< ProgramInfo * > * m_recordingList
void SetText(const QString &text, const QString &name="", const QString &state="")
QDateTime GetScheduledEndTime(void) const
The scheduled end time of the program.
Definition: programinfo.h:389
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
MythUIButton * m_okButton
CheckState state() const
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
QList< ProgramInfo * > m_selectedList
MythUIType * GetFocusWidget(void) const
QString type
Definition: archiveutil.h:54
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
virtual QString GetValue() const
QString subtitle
Definition: archiveutil.h:56
static QString formatSize(int64_t sizeKB, int prec)
QString videoCodec
Definition: archiveutil.h:67
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
MythUIText * m_descriptionText
QString startDate
Definition: archiveutil.h:58
QString title
Definition: archiveutil.h:55
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
MythUIText * m_datetimeText
EncoderProfile * encoderProfile
Definition: archiveutil.h:65
void SetReturnEvent(QObject *retobject, const QString &resultid)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
QString GetPlaybackURL(bool checkMaster=false, bool forceCheckLocal=false) const
Returns filename or URL to be used to play back this recording.
Default local time.
Definition: mythdate.h:20
int videoHeight
Definition: archiveutil.h:68
QString description
Definition: archiveutil.h:57
void SetItemCurrent(MythUIButtonListItem *item)
bool SetFocusWidget(MythUIType *widget=nullptr)
GetRecordingListThread(RecordingSelector *parent)
QList< ArchiveItem * > * m_archiveList
int64_t size
Definition: archiveutil.h:61
MythUIButtonListItem * GetItemFirst() const
MythUIButton * m_cancelButton
vector< ProgramInfo * > * RemoteGetRecordedList(int sort)
Definition: remoteutil.cpp:16
QString GetPathname(void) const
Definition: programinfo.h:335
void DisplayState(const QString &state, const QString &name)
MythUIImage * m_cutlistImage
void setChecked(CheckState state)
void setCategory(MythUIButtonListItem *item)
QStringList m_categories
MythUIButtonListItem * GetItemCurrent() const
bool Create(void) override
uint GetEpisode(void) const
Definition: programinfo.h:359