MythTV  master
videofileassoc.cpp
Go to the documentation of this file.
1 // C++
2 #include <algorithm>
3 #include <functional> //mem_fun
4 #include <iterator>
5 #include <map>
6 #include <random>
7 #include <utility>
8 #include <vector>
9 
10 // MythTV
16 #include "libmythui/mythuibutton.h"
20 
21 // MythFrontend
22 #include "videofileassoc.h"
23 
24 namespace
25 {
26  template <typename T, typename Inst, typename FuncType>
27  void assign_if_changed_notify(T &oldVal, const T &newVal, Inst *inst,
28  FuncType func)
29  {
30  if (oldVal != newVal)
31  {
32  oldVal = newVal;
33  func(inst);
34  }
35  }
36 
37  class FileAssociationWrap
38  {
39  public:
40  enum FA_State {
41  efsNONE,
42  efsDELETE,
43  efsSAVE
44  };
45 
46  public:
47  explicit FileAssociationWrap(const QString &new_extension) : m_state(efsSAVE)
48  {
49  m_fa.extension = new_extension;
50  }
51 
52  explicit FileAssociationWrap(FileAssociations::file_association fa) :
53  m_fa(std::move(fa)) {}
54 
55  unsigned int GetIDx(void) const { return m_fa.id; }
56  QString GetExtension(void) const { return m_fa.extension; }
57  QString GetCommand(void) const { return m_fa.playcommand; }
58  bool GetDefault(void) const { return m_fa.use_default; }
59  bool GetIgnore(void) const { return m_fa.ignore; }
60 
61  FA_State GetState() const { return m_state; }
62 
63  void CommitChanges()
64  {
65  switch (m_state)
66  {
67  case efsDELETE:
68  {
70  m_fa.id = 0;
71  m_state = efsNONE;
72  break;
73  }
74  case efsSAVE:
75  {
77  {
78  m_state = efsNONE;
79  }
80  break;
81  }
82  case efsNONE:
83  default: {}
84  }
85  }
86 
87  void MarkForDeletion()
88  {
89  m_state = efsDELETE;
90  }
91 
92  void SetDefault(bool yes_or_no)
93  {
94  assign_if_changed_notify(m_fa.use_default, yes_or_no, this,
95  std::mem_fn(&FileAssociationWrap::SetChanged));
96  }
97 
98  void SetIgnore(bool yes_or_no)
99  {
100  assign_if_changed_notify(m_fa.ignore, yes_or_no, this,
101  std::mem_fn(&FileAssociationWrap::SetChanged));
102  }
103 
104  void SetCommand(const QString &new_command)
105  {
106  assign_if_changed_notify(m_fa.playcommand, new_command, this,
107  std::mem_fn(&FileAssociationWrap::SetChanged));
108  }
109 
110  private:
111  void SetChanged() { m_state = efsSAVE; }
112 
113  private:
115  FA_State m_state {efsNONE};
116  };
117 
118  class BlockSignalsGuard
119  {
120  public:
121  void Block(QObject *o)
122  {
123  o->blockSignals(true);
124  m_objects.push_back(o);
125  }
126 
127  ~BlockSignalsGuard()
128  {
129  for (auto & obj : m_objects)
130  obj->blockSignals(false);
131  }
132 
133  private:
134  using list_type = std::vector<QObject *>;
135 
136  private:
137  list_type m_objects;
138  };
139 
140  struct UIDToFAPair
141  {
142  using UID_type = unsigned int;
143 
144  UIDToFAPair() = default;
145 
146  UIDToFAPair(UID_type uid, FileAssociationWrap *assoc) :
147  m_uid(uid), m_fileAssoc(assoc) {}
148 
149  UID_type m_uid {0};
150  FileAssociationWrap *m_fileAssoc {nullptr};
151  };
152 
153 
154  bool operator<(const UIDToFAPair lhs, const UIDToFAPair rhs)
155  {
156  if (lhs.m_fileAssoc && rhs.m_fileAssoc)
157  return QString::localeAwareCompare(lhs.m_fileAssoc->GetExtension(),
158  rhs.m_fileAssoc->GetExtension()) < 0;
159 
160  return rhs.m_fileAssoc;
161  }
162 }
163 
165 
167 {
168  public:
169  using UIReadyList_type = std::vector<UIDToFAPair>;
170 
171  public:
173  {
175  }
176 
178  {
179  for (auto & fa : m_fileAssociations)
180  delete fa.second;
181  }
182 
184  {
185  for (auto & fa : m_fileAssociations)
186  fa.second->CommitChanges();
187  }
188 
189  bool AddExtension(const QString& newExtension, UIDToFAPair::UID_type &new_id)
190  {
191  if (!newExtension.isEmpty())
192  {
193  new_id = ++m_nextFAID;
194  m_fileAssociations.insert(FA_collection::value_type(new_id,
195  new FileAssociationWrap(newExtension)));
196  return true;
197  }
198 
199  return false;
200  }
201 
202  bool DeleteExtension(UIDToFAPair::UID_type uid)
203  {
204  auto p = m_fileAssociations.find(uid);
205  if (p != m_fileAssociations.end())
206  {
207  p->second->MarkForDeletion();
208 
209  return true;
210  }
211 
212  return false;
213  }
214 
215  // Returns a list sorted by extension
217  {
218  UIReadyList_type ret;
219  std::transform(m_fileAssociations.begin(), m_fileAssociations.end(),
220  std::back_inserter(ret), fa_col_ent_2_UIDFAPair());
221  auto deleted = std::remove_if(ret.begin(),
223 
224  if (deleted != ret.end())
225  ret.erase(deleted, ret.end());
226 
227  std::sort(ret.begin(), ret.end());
228 
229  return ret;
230  }
231 
232  static FileAssociationWrap *GetCurrentFA(MythUIButtonList *buttonList)
233  {
234  MythUIButtonListItem *item = buttonList->GetItemCurrent();
235  if (item)
236  {
237  auto key = item->GetData().value<UIDToFAPair>();
238  if (key.m_fileAssoc)
239  {
240  return key.m_fileAssoc;
241  }
242  }
243 
244  return nullptr;
245  }
246 
247  void SetSelectionOverride(UIDToFAPair::UID_type new_sel)
248  {
249  m_selectionOverride = new_sel;
250  }
251 
252  UIDToFAPair::UID_type GetSelectionOverride() const
253  {
254  return m_selectionOverride;
255  }
256 
257  private:
258  using FA_collection = std::map<UIDToFAPair::UID_type, FileAssociationWrap *>;
259 
260  private:
262  {
263  UIDToFAPair operator()(
264  const FileAssocDialogPrivate::FA_collection::value_type from)
265  {
266  return {from.first, from.second};
267  }
268  };
269 
270  template <FileAssociationWrap::FA_State against>
272  {
273  bool operator()(const UIDToFAPair item)
274  {
275  return item.m_fileAssoc && item.m_fileAssoc->GetState() == against;
276  }
277  };
278 
280  {
281  using tmp_fa_list = std::vector<UIDToFAPair>;
282 
283  const FileAssociations::association_list &fa_list =
285  tmp_fa_list tmp_fa;
286  tmp_fa.reserve(fa_list.size());
287 
288  auto newpair = [this](const auto & fa)
289  { return UIDToFAPair(++m_nextFAID, new FileAssociationWrap(fa)); };
290  std::transform(fa_list.cbegin(), fa_list.cend(), std::back_inserter(tmp_fa), newpair);
291 
292  std::shuffle(tmp_fa.begin(), tmp_fa.end(),
293  std::mt19937(std::random_device()()));
294 
295  for (const auto& fa : tmp_fa)
296  {
297  m_fileAssociations.insert(FA_collection::value_type(fa.m_uid,
298  fa.m_fileAssoc));
299  }
300 
301  if (m_fileAssociations.empty())
302  {
303  LOG(VB_GENERAL, LOG_ERR,
304  QString("%1: Couldn't get any filetypes from your database.")
305  .arg(__FILE__));
306  }
307  }
308 
309  private:
311  UIDToFAPair::UID_type m_nextFAID {0};
312  UIDToFAPair::UID_type m_selectionOverride {0};
313 };
314 
316 
318  const QString &lname) :
319  MythScreenType(screenParent, lname),
320  m_private(new FileAssocDialogPrivate)
321 {
322 }
323 
325 {
326  delete m_private;
327 }
328 
330 {
331  if (!LoadWindowFromXML("video-ui.xml", "file_associations", this))
332  return false;
333 
334  bool err = false;
335  UIUtilE::Assign(this, m_extensionList, "extension_select", &err);
336  UIUtilE::Assign(this, m_commandEdit, "command", &err);
337  UIUtilE::Assign(this, m_ignoreCheck, "ignore_check", &err);
338  UIUtilE::Assign(this, m_defaultCheck, "default_check", &err);
339 
340  UIUtilE::Assign(this, m_doneButton, "done_button", &err);
341  UIUtilE::Assign(this, m_newButton, "new_button", &err);
342  UIUtilE::Assign(this, m_deleteButton, "delete_button", &err);
343 
344  if (err)
345  {
346  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'file_associations'");
347  return false;
348  }
349 
356 
361 
362  m_extensionList->SetHelpText(tr("Select a file extension from this list "
363  "to modify or delete its settings."));
364  m_commandEdit->SetHelpText(tr("The command to use when playing this kind "
365  "of file. To use MythTV's Internal player, "
366  "use \"Internal\" as the player. For all other "
367  "players, you can use %s to substitute the filename."));
368  m_ignoreCheck->SetHelpText(tr("When checked, this will cause the file extension "
369  "to be ignored in scans of your library."));
370  m_defaultCheck->SetHelpText(tr("When checked, this will cause the global player "
371  "settings to override this one."));
372  m_doneButton->SetHelpText(tr("Save and exit this screen."));
373  m_newButton->SetHelpText(tr("Create a new file extension."));
374  m_deleteButton->SetHelpText(tr("Delete this file extension."));
375 
376  UpdateScreen();
377 
378  BuildFocusList();
379 
380  return true;
381 }
382 
384 {
385  (void) item;
386  UpdateScreen();
387 }
388 
390 {
391  FileAssociationWrap *wrap = FileAssocDialogPrivate::GetCurrentFA(m_extensionList);
392  if (wrap != nullptr)
393  wrap->SetDefault(m_defaultCheck->GetBooleanCheckState());
394 }
395 
397 {
398  FileAssociationWrap *wrap = FileAssocDialogPrivate::GetCurrentFA(m_extensionList);
399  if (wrap != nullptr)
400  wrap->SetIgnore(m_ignoreCheck->GetBooleanCheckState());
401 }
402 
404 {
405  FileAssociationWrap *wrap = FileAssocDialogPrivate::GetCurrentFA(m_extensionList);
406  if (wrap != nullptr)
407  wrap->SetCommand(m_commandEdit->GetText());
408 }
409 
411 {
413  Close();
414 }
415 
417 {
419  if (item)
420  {
421  auto key = item->GetData().value<UIDToFAPair>();
422  if (key.m_fileAssoc && m_private->DeleteExtension(key.m_uid))
423  delete item;
424  }
425 
426  UpdateScreen();
427 }
428 
430 {
431  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
432 
433  QString message = tr("Enter the new extension:");
434 
435  auto *newextdialog = new MythTextInputDialog(popupStack, message);
436 
437  if (newextdialog->Create())
438  popupStack->AddScreen(newextdialog);
439 
440  connect(newextdialog, &MythTextInputDialog::haveResult,
442 }
443 
444 void FileAssocDialog::OnNewExtensionComplete(const QString& newExtension)
445 {
446  UIDToFAPair::UID_type new_sel = 0;
447  if (m_private->AddExtension(newExtension, new_sel))
448  {
450  UpdateScreen(true);
451  }
452 }
453 
454 void FileAssocDialog::UpdateScreen(bool useSelectionOverride /* = false*/)
455 {
456  BlockSignalsGuard bsg;
457  bsg.Block(m_extensionList);
458  bsg.Block(m_commandEdit);
459  bsg.Block(m_defaultCheck);
460  bsg.Block(m_ignoreCheck);
461 
464 
465  if (tmp_list.empty())
466  {
467  m_extensionList->SetVisible(false);
468  m_commandEdit->SetVisible(false);
469  m_defaultCheck->SetVisible(false);
470  m_ignoreCheck->SetVisible(false);
471  m_deleteButton->SetVisible(false);
472  }
473  else
474  {
475  UIDToFAPair::UID_type selected_id = 0;
477  if (current_item)
478  {
479  auto key = current_item->GetData().value<UIDToFAPair>();
480  if (key.m_fileAssoc)
481  {
482  selected_id = key.m_uid;
483  }
484  }
485 
486  if (useSelectionOverride)
487  selected_id = m_private->GetSelectionOverride();
488 
491 
492  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
493  for (auto & fad : tmp_list)
494  {
495  if (fad.m_fileAssoc)
496  {
497  // No memory leak. MythUIButtonListItem adds the new
498  // item into m_extensionList.
499  auto *new_item = new MythUIButtonListItem(m_extensionList,
500  fad.m_fileAssoc->GetExtension(),
501  QVariant::fromValue(fad));
502  if (selected_id && fad.m_uid == selected_id)
503  m_extensionList->SetItemCurrent(new_item);
504  }
505  }
506 
507  current_item = m_extensionList->GetItemCurrent();
508  if (current_item)
509  {
510  auto key = current_item->GetData().value<UIDToFAPair>();
511  if (key.m_fileAssoc)
512  {
513  m_commandEdit->SetVisible(true);
514  m_commandEdit->SetText(key.m_fileAssoc->GetCommand());
515 
516  m_defaultCheck->SetVisible(true);
517  m_defaultCheck->SetCheckState(key.m_fileAssoc->GetDefault() ?
519 
520  m_ignoreCheck->SetVisible(true);
521  m_ignoreCheck->SetCheckState(key.m_fileAssoc->GetIgnore() ?
523 
524  m_deleteButton->SetVisible(true);
525  }
526  }
527  }
528 
529  BuildFocusList();
530 }
531 
532 Q_DECLARE_METATYPE(UIDToFAPair)
MythUIButton::Clicked
void Clicked()
FileAssocDialogPrivate::FileAssocDialogPrivate
FileAssocDialogPrivate()
Definition: videofileassoc.cpp:172
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1587
FileAssocDialog::OnFASelected
void OnFASelected(MythUIButtonListItem *item)
Definition: videofileassoc.cpp:383
FileAssocDialogPrivate::DeleteExtension
bool DeleteExtension(UIDToFAPair::UID_type uid)
Definition: videofileassoc.cpp:202
FileAssocDialog::UpdateScreen
void UpdateScreen(bool useSelectionOverride=false)
Definition: videofileassoc.cpp:454
FileAssocDialog::m_ignoreCheck
MythUICheckBox * m_ignoreCheck
Definition: videofileassoc.h:43
FileAssocDialogPrivate::GetSelectionOverride
UIDToFAPair::UID_type GetSelectionOverride() const
Definition: videofileassoc.cpp:252
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:386
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
FileAssocDialog::OnDonePressed
void OnDonePressed()
Definition: videofileassoc.cpp:410
FileAssocDialogPrivate::AddExtension
bool AddExtension(const QString &newExtension, UIDToFAPair::UID_type &new_id)
Definition: videofileassoc.cpp:189
MythUICheckBox::GetBooleanCheckState
bool GetBooleanCheckState(void) const
Definition: mythuicheckbox.cpp:103
FileAssocDialogPrivate::UIReadyList_type
std::vector< UIDToFAPair > UIReadyList_type
Definition: videofileassoc.cpp:169
mythdialogbox.h
MythScreenStack
Definition: mythscreenstack.h:16
FileAssociations::remove
bool remove(unsigned int id)
Definition: dbaccess.cpp:801
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
FileAssociations::getList
const association_list & getList() const
Definition: dbaccess.cpp:806
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
MythUITextEdit::GetText
QString GetText(void) const
Definition: mythuitextedit.h:50
FileAssocDialogPrivate::fa_col_ent_2_UIDFAPair
Definition: videofileassoc.cpp:261
FileAssocDialogPrivate::GetUIReadyList
UIReadyList_type GetUIReadyList()
Definition: videofileassoc.cpp:216
MythTextInputDialog::haveResult
void haveResult(QString)
mythuibuttonlist.h
FileAssocDialogPrivate::SetSelectionOverride
void SetSelectionOverride(UIDToFAPair::UID_type new_sel)
Definition: videofileassoc.cpp:247
FileAssocDialog::OnNewExtensionComplete
void OnNewExtensionComplete(const QString &newExtension)
Definition: videofileassoc.cpp:444
dbaccess.h
Q_DECLARE_METATYPE
Q_DECLARE_METATYPE(StandardSetting *)
FileAssocDialogPrivate::fa_col_ent_2_UIDFAPair::operator()
UIDToFAPair operator()(const FileAssocDialogPrivate::FA_collection::value_type from)
Definition: videofileassoc.cpp:263
FileAssocDialog::~FileAssocDialog
~FileAssocDialog() override
Definition: videofileassoc.cpp:324
MythUIButtonListItem
Definition: mythuibuttonlist.h:41
MythUITextEdit::SetText
void SetText(const QString &text, bool moveCursor=true)
Definition: mythuitextedit.cpp:197
FileAssocDialog::m_defaultCheck
MythUICheckBox * m_defaultCheck
Definition: videofileassoc.h:42
FileAssocDialog::m_newButton
MythUIButton * m_newButton
Definition: videofileassoc.h:45
MythUICheckBox::valueChanged
void valueChanged()
FileAssocDialogPrivate::m_selectionOverride
UIDToFAPair::UID_type m_selectionOverride
Definition: videofileassoc.cpp:312
mythlogging.h
FileAssocDialog::OnUseDefaltChanged
void OnUseDefaltChanged()
Definition: videofileassoc.cpp:389
hardwareprofile.config.p
p
Definition: config.py:33
FileAssocDialog::m_private
class FileAssocDialogPrivate * m_private
Definition: videofileassoc.h:48
FileAssocDialogPrivate::SaveFileAssociations
void SaveFileAssociations()
Definition: videofileassoc.cpp:183
FileAssociations::association_list
std::vector< file_association > association_list
Definition: dbaccess.h:154
MythUIType::SetHelpText
void SetHelpText(const QString &text)
Definition: mythuitype.h:175
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:206
FileAssocDialogPrivate::test_fa_state::operator()
bool operator()(const UIDToFAPair item)
Definition: videofileassoc.cpp:273
videofileassoc.h
FileAssocDialogPrivate::~FileAssocDialogPrivate
~FileAssocDialogPrivate()
Definition: videofileassoc.cpp:177
FileAssocDialog::FileAssocDialog
FileAssocDialog(MythScreenStack *screenParent, const QString &lname)
Definition: videofileassoc.cpp:317
GetState
#define GetState(a, b)
Definition: audiooutputopensles.cpp:47
MythUIStateType::Full
@ Full
Definition: mythuistatetype.h:25
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3665
FileAssocDialog::m_commandEdit
MythUITextEdit * m_commandEdit
Definition: videofileassoc.h:40
UIUtilDisp::Assign
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
FileAssocDialogPrivate::FA_collection
std::map< UIDToFAPair::UID_type, FileAssociationWrap * > FA_collection
Definition: videofileassoc.cpp:258
FileAssociations::getFileAssociation
static FileAssociations & getFileAssociation()
Definition: dbaccess.cpp:831
operator<
bool operator<(const ParentalLevel &lhs, const ParentalLevel &rhs)
Definition: parentalcontrols.cpp:143
FileAssocDialogPrivate
Definition: videofileassoc.cpp:166
MythUIStateType::Off
@ Off
Definition: mythuistatetype.h:25
mythuitextedit.h
MythUICheckBox::SetCheckState
void SetCheckState(MythUIStateType::StateType state)
Definition: mythuicheckbox.cpp:66
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:695
FileAssocDialog::OnDeletePressed
void OnDeletePressed()
Definition: videofileassoc.cpp:416
FileAssocDialog::m_deleteButton
MythUIButton * m_deleteButton
Definition: videofileassoc.h:46
std
Definition: mythchrono.h:23
FileAssocDialog::OnPlayerCommandChanged
void OnPlayerCommandChanged()
Definition: videofileassoc.cpp:403
MythUIType::SetVisible
virtual void SetVisible(bool visible)
Definition: mythuitype.cpp:1108
MythUIButtonList::Reset
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuibuttonlist.cpp:116
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
MythUIButtonList::SetItemCurrent
void SetItemCurrent(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1554
mythuibutton.h
FileAssocDialogPrivate::m_fileAssociations
FA_collection m_fileAssociations
Definition: videofileassoc.cpp:310
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:320
MythUITextEdit::valueChanged
void valueChanged()
FileAssocDialog::m_doneButton
MythUIButton * m_doneButton
Definition: videofileassoc.h:44
FileAssocDialog::m_extensionList
MythUIButtonList * m_extensionList
Definition: videofileassoc.h:41
MythTextInputDialog
Dialog prompting the user to enter a text string.
Definition: mythdialogbox.h:314
FileAssociations::file_association
Definition: dbaccess.h:139
mythuicheckbox.h
FileAssocDialogPrivate::test_fa_state
Definition: videofileassoc.cpp:271
FileAssocDialogPrivate::m_nextFAID
UIDToFAPair::UID_type m_nextFAID
Definition: videofileassoc.cpp:311
MythUIButtonList
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
Definition: mythuibuttonlist.h:191
FileAssocDialogPrivate::LoadFileAssociations
void LoadFileAssociations()
Definition: videofileassoc.cpp:279
mythmainwindow.h
FileAssocDialogPrivate::GetCurrentFA
static FileAssociationWrap * GetCurrentFA(MythUIButtonList *buttonList)
Definition: videofileassoc.cpp:232
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:50
FileAssocDialog::Create
bool Create() override
Definition: videofileassoc.cpp:329
videoutils.h
FileAssocDialog::OnNewExtensionPressed
void OnNewExtensionPressed() const
Definition: videofileassoc.cpp:429
FileAssocDialog::OnIgnoreChanged
void OnIgnoreChanged()
Definition: videofileassoc.cpp:396