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 <QApplication>
8#include <QDir>
9#include <QKeyEvent>
10#include <QTimer>
11
12// mythtv
13#include <libmythbase/mthread.h>
15#include <libmythbase/mythdb.h>
28
29// mytharchive
30#include "archiveutil.h"
31#include "recordingselector.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
87
88 new MythUIButtonListItem(m_categorySelector, tr("All Recordings"));
91
96
99
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 auto *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
124 auto *thread = new GetRecordingListThread(this);
125 while (thread->isRunning())
126 {
127 QCoreApplication::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
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 const QString& action = actions[i];
161 handled = true;
162
163 if (action == "MENU")
164 {
165 ShowMenu();
166 }
167 else
168 {
169 handled = false;
170 }
171 }
172
173 if (!handled && MythScreenType::keyPressEvent(event))
174 handled = true;
175
176 return handled;
177}
178
180{
181 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
182
183 auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
184
185 if (menuPopup->Create())
186 popupStack->AddScreen(menuPopup);
187
188 menuPopup->SetReturnEvent(this, "action");
189
190 menuPopup->AddButton(tr("Clear All"), &RecordingSelector::clearAll);
191 menuPopup->AddButton(tr("Select All"), &RecordingSelector::selectAll);
192}
193
195{
196 while (!m_selectedList.isEmpty())
197 m_selectedList.takeFirst();
198 m_selectedList.clear();
199
200 for (auto *p : *m_recordingList)
201 m_selectedList.append(p);
202
204}
205
207{
208 while (!m_selectedList.isEmpty())
209 m_selectedList.takeFirst();
210 m_selectedList.clear();
211
213}
214
216{
218 {
219 int index = m_selectedList.indexOf(item->GetData().value<ProgramInfo *>());
220 if (index != -1)
221 m_selectedList.takeAt(index);
223 }
224 else
225 {
226 int index = m_selectedList.indexOf(item->GetData().value<ProgramInfo *>());
227 if (index == -1)
228 m_selectedList.append(item->GetData().value<ProgramInfo *>());
229
231 }
232}
233
235{
236 auto *p = item->GetData().value<ProgramInfo *>();
237
238 if (!p)
239 return;
240
241 if (m_titleText)
242 m_titleText->SetText(p->GetTitle());
243
244 if (m_datetimeText)
245 m_datetimeText->SetText(p->GetScheduledStartTime().toLocalTime()
246 .toString("dd MMM yy (hh:mm)"));
247
249 {
251 ((!p->GetSubtitle().isEmpty()) ? p->GetSubtitle() + "\n" : "") +
252 p->GetDescription());
253 }
254
255 if (m_filesizeText)
256 {
257 m_filesizeText->SetText(StringUtil::formatKBytes(p->GetFilesize() / 1024));
258 }
259
260 if (m_cutlistImage)
261 {
262 if (p->HasCutlist())
264 else
266 }
267
268 if (m_previewImage)
269 {
270 // try to locate a preview image
271 if (QFile::exists(p->GetPathname() + ".png"))
272 {
273 m_previewImage->SetFilename(p->GetPathname() + ".png");
275 }
276 else
277 {
278 m_previewImage->SetFilename("blank.png");
280 }
281 }
282}
283
285{
286 // loop though selected recordings and add them to the list
287 // remove any items that have been removed from the list
288 QList<ArchiveItem *> tempAList;
289 for (auto *a : std::as_const(*m_archiveList))
290 {
291 bool found = false;
292
293 for (auto *p : std::as_const(m_selectedList))
294 {
295 if (a->type != "Recording" || a->filename == p->GetPlaybackURL(false, true))
296 {
297 found = true;
298 break;
299 }
300 }
301
302 if (!found)
303 tempAList.append(a);
304 }
305
306 for (auto *x : std::as_const(tempAList))
307 m_archiveList->removeAll(x);
308
309 // remove any items that are already in the list
310 QList<ProgramInfo *> tempSList;
311 for (auto *p : std::as_const(m_selectedList))
312 {
313 for (const auto *a : std::as_const(*m_archiveList))
314 {
315 if (a->filename == p->GetPlaybackURL(false, true))
316 {
317 tempSList.append(p);
318 break;
319 }
320 }
321 }
322
323 for (auto *x : std::as_const(tempSList))
324 m_selectedList.removeAll(x);
325
326 // add all that are left
327 for (auto *p : std::as_const(m_selectedList))
328 {
329 auto *a = new ArchiveItem;
330 a->type = "Recording";
331 a->title = p->GetTitle();
332 a->subtitle = p->GetSubtitle();
333 a->description = p->GetDescription();
334 a->startDate = p->GetScheduledStartTime()
335 .toLocalTime().toString("dd MMM yy");
336 a->startTime = p->GetScheduledStartTime()
337 .toLocalTime().toString("(hh:mm)");
338 a->size = p->GetFilesize();
339 a->filename = p->GetPlaybackURL(false, true);
340 a->hasCutlist = p->HasCutlist();
341 a->useCutlist = false;
342 a->duration = 0;
343 a->cutDuration = 0;
344 a->videoWidth = 0;
345 a->videoHeight = 0;
346 a->fileCodec = "";
347 a->videoCodec = "";
348 a->encoderProfile = nullptr;
349 a->editedDetails = false;
350 m_archiveList->append(a);
351 }
352
353 emit haveResult(true);
354 Close();
355}
356
358{
359 emit haveResult(false);
360 Close();
361}
362
364{
365 if (!m_recordingList || m_recordingList->empty())
366 return;
367
369
371 {
372 for (auto *p : *m_recordingList)
373 {
374 if (p->GetTitle() == m_categorySelector->GetValue() ||
375 m_categorySelector->GetValue() == tr("All Recordings"))
376 {
377 auto* item = new MythUIButtonListItem(
379 p->GetTitle() + " ~ " +
380 p->GetScheduledStartTime().toLocalTime()
381 .toString("dd MMM yy (hh:mm)"));
382 item->setCheckable(true);
383 if (m_selectedList.indexOf(p) != -1)
384 {
385 item->setChecked(MythUIButtonListItem::FullChecked);
386 }
387 else
388 {
389 item->setChecked(MythUIButtonListItem::NotChecked);
390 }
391
392 QString title = p->GetTitle();
393 QString subtitle = p->GetSubtitle();
394
395 QDateTime recstartts = p->GetScheduledStartTime();
396 QDateTime recendts = p->GetScheduledEndTime();
397
398 QString timedate = QString("%1 - %2")
401
402 uint season = p->GetSeason();
403 uint episode = p->GetEpisode();
404 QString seasone;
405 QString seasonx;
406
407 if (season && episode)
408 {
409 seasone = QString("s%1e%2")
410 .arg(StringUtil::intToPaddedString(season, 2),
412 seasonx = QString("%1x%2")
413 .arg(StringUtil::intToPaddedString(season, 1),
415 }
416
417 item->SetText(title, "title");
418 item->SetText(subtitle, "subtitle");
419 if (subtitle.isEmpty())
420 item->SetText(title, "titlesubtitle");
421 else
422 item->SetText(title + " - \"" + subtitle + '"',
423 "titlesubtitle");
424
425 item->SetText(timedate, "timedate");
426 item->SetText(p->GetDescription(), "description");
427 item->SetText(StringUtil::formatKBytes(p->GetFilesize() / 1024),
428 "filesize_str");
429
430 item->SetText(QString::number(season), "season");
431 item->SetText(QString::number(episode), "episode");
432 item->SetText(seasonx, "00x00");
433 item->SetText(seasone, "s00e00");
434
435 item->DisplayState(p->HasCutlist() ? "yes" : "no", "cutlist");
436
437 item->SetData(QVariant::fromValue(p));
438 }
439 QCoreApplication::processEvents();
440 }
441 }
442
445}
446
448{
450 m_categories.clear();
451
452 if (m_recordingList && !m_recordingList->empty())
453 {
454 auto i = m_recordingList->begin();
455 for ( ; i != m_recordingList->end(); ++i)
456 {
457 ProgramInfo *p = *i;
458 // ignore live tv and deleted recordings
459 if (p->GetRecordingGroup() == "LiveTV" ||
460 p->GetRecordingGroup() == "Deleted")
461 {
462 i = m_recordingList->erase(i);
463 --i;
464 continue;
465 }
466
467 if (m_categories.indexOf(p->GetTitle()) == -1)
468 m_categories.append(p->GetTitle());
469 }
470 }
471}
472
474{
475 // sort and add categories to selector
476 m_categories.sort();
477
479 {
480 new MythUIButtonListItem(m_categorySelector, tr("All Recordings"));
482
483 for (int x = 0; x < m_categories.count(); x++)
484 {
486 }
487 }
488}
489
491{
493}
494
496{
497 if (!m_recordingList)
498 return;
499
500 m_selectedList.clear();
501
502 for (const auto *a : std::as_const(*m_archiveList))
503 {
504 for (auto *p : *m_recordingList)
505 {
506 if (p->GetPlaybackURL(false, true) == a->filename)
507 {
508 if (m_selectedList.indexOf(p) == -1)
509 m_selectedList.append(p);
510 break;
511 }
512
513 QCoreApplication::processEvents();
514 }
515 }
516}
RecordingSelector * m_parent
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
GetRecordingListThread(RecordingSelector *parent)
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
Basic menu dialog, message and a list of options.
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
void BuildFocusList(void)
MythUIType * GetFocusWidget(void) const
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
bool SetFocusWidget(MythUIType *widget=nullptr)
virtual void Close()
CheckState state() const
void setChecked(CheckState state)
virtual QString GetValue() const
MythUIButtonListItem * GetItemCurrent() const
void SetItemCurrent(MythUIButtonListItem *item)
MythUIButtonListItem * GetItemFirst() const
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
void itemClicked(MythUIButtonListItem *item)
void itemSelected(MythUIButtonListItem *item)
void Clicked()
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:115
void Hide(void)
void Show(void)
Holds information on recordings and videos.
Definition: programinfo.h:70
MythUIText * m_descriptionText
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
void toggleSelected(MythUIButtonListItem *item)
QList< ProgramInfo * > m_selectedList
bool Create() override
void haveResult(bool ok)
void updateCategorySelector(void)
MythUIText * m_datetimeText
friend class GetRecordingListThread
void titleChanged(MythUIButtonListItem *item)
void setCategory(MythUIButtonListItem *item)
MythUIImage * m_cutlistImage
MythUIButtonList * m_categorySelector
std::vector< ProgramInfo * > * m_recordingList
MythUIButton * m_cancelButton
~RecordingSelector(void) override
QStringList m_categories
QList< ArchiveItem * > * m_archiveList
void updateRecordingList(void)
MythUIImage * m_previewImage
MythUIButton * m_okButton
MythUIButtonList * m_recordingButtonList
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
MythUIText * m_filesizeText
void ShowMenu(void) override
MythUIText * m_titleText
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
unsigned int uint
Definition: compat.h:68
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kDateTimeFull
Default local time.
Definition: mythdate.h:23
@ kTime
Default local time.
Definition: mythdate.h:22
QString intToPaddedString(int n, int width=2)
Creates a zero padded string representation of an integer.
Definition: stringutil.h:27
MBASE_PUBLIC QString formatKBytes(int64_t sizeKB, int prec=1)
Definition: stringutil.cpp:357
bool exists(str path)
Definition: xbmcvfs.py:51
std::vector< ProgramInfo * > * RemoteGetRecordedList(int sort)
QString type
Definition: archiveutil.h:53
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27