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