MythTV  0.26-pre
videofileassoc.cpp
Go to the documentation of this file.
00001 #include <algorithm>
00002 #include <vector>
00003 #include <iterator>
00004 #include <map>
00005 
00006 #include "mythlogging.h"
00007 #include "mythmainwindow.h"
00008 #include "mythdialogbox.h"
00009 #include "mythuibuttonlist.h"
00010 #include "mythuitextedit.h"
00011 #include "mythuicheckbox.h"
00012 #include "mythuibutton.h"
00013 #include "dbaccess.h"
00014 #include "videoutils.h"
00015 
00016 #include "videofileassoc.h"
00017 
00018 namespace
00019 {
00020     template <typename T, typename Inst, typename FuncType>
00021     void assign_if_changed_notify(T &oldVal, const T &newVal, Inst *inst,
00022             FuncType func)
00023     {
00024         if (oldVal != newVal)
00025         {
00026             oldVal = newVal;
00027             func(inst);
00028         }
00029     }
00030 
00031     class FileAssociationWrap
00032     {
00033       public:
00034         enum FA_State {
00035             efsNONE,
00036             efsDELETE,
00037             efsSAVE
00038         };
00039 
00040       public:
00041         FileAssociationWrap(const QString &new_extension) : m_state(efsSAVE)
00042         {
00043             m_fa.extension = new_extension;
00044         }
00045 
00046         FileAssociationWrap(const FileAssociations::file_association &fa) :
00047             m_fa(fa), m_state(efsNONE) {}
00048 
00049         int GetID() const { return m_fa.id; }
00050         QString GetExtension() const { return m_fa.extension; }
00051         QString GetCommand() const { return m_fa.playcommand; }
00052         bool GetDefault() const { return m_fa.use_default; }
00053         bool GetIgnore() const { return m_fa.ignore; }
00054 
00055         FA_State GetState() const { return m_state; }
00056 
00057         void CommitChanges()
00058         {
00059             switch (m_state)
00060             {
00061                 case efsDELETE:
00062                 {
00063                     FileAssociations::getFileAssociation().remove(m_fa.id);
00064                     m_fa.id = -1;
00065                     m_state = efsNONE;
00066                     break;
00067                 }
00068                 case efsSAVE:
00069                 {
00070                     if (FileAssociations::getFileAssociation().add(m_fa))
00071                     {
00072                         m_state = efsNONE;
00073                     }
00074                     break;
00075                 }
00076                 case efsNONE:
00077                 default: {}
00078             }
00079         }
00080 
00081         void MarkForDeletion()
00082         {
00083             m_state = efsDELETE;
00084         }
00085 
00086         void SetDefault(bool yes_or_no)
00087         {
00088             assign_if_changed_notify(m_fa.use_default, yes_or_no, this,
00089                     std::mem_fun(&FileAssociationWrap::SetChanged));
00090         }
00091 
00092         void SetIgnore(bool yes_or_no)
00093         {
00094             assign_if_changed_notify(m_fa.ignore, yes_or_no, this,
00095                     std::mem_fun(&FileAssociationWrap::SetChanged));
00096         }
00097 
00098         void SetCommand(const QString &new_command)
00099         {
00100             assign_if_changed_notify(m_fa.playcommand, new_command, this,
00101                     std::mem_fun(&FileAssociationWrap::SetChanged));
00102         }
00103 
00104       private:
00105         void SetChanged() { m_state = efsSAVE; }
00106 
00107       private:
00108         FileAssociations::file_association m_fa;
00109         FA_State m_state;
00110     };
00111 
00112     class BlockSignalsGuard
00113     {
00114       public:
00115         void Block(QObject *o)
00116         {
00117             o->blockSignals(true);
00118             m_objects.push_back(o);
00119         }
00120 
00121         ~BlockSignalsGuard()
00122         {
00123             for (list_type::iterator p = m_objects.begin();
00124                     p != m_objects.end(); ++p)
00125             {
00126                 (*p)->blockSignals(false);
00127             }
00128         }
00129 
00130       private:
00131         typedef std::vector<QObject *> list_type;
00132 
00133       private:
00134         list_type m_objects;
00135     };
00136 
00137     struct UIDToFAPair
00138     {
00139         typedef unsigned int UID_type;
00140 
00141         UIDToFAPair() : m_uid(0), m_file_assoc(0) {}
00142 
00143         UIDToFAPair(UID_type uid, FileAssociationWrap *assoc) :
00144             m_uid(uid), m_file_assoc(assoc) {}
00145 
00146         UID_type m_uid;
00147         FileAssociationWrap *m_file_assoc;
00148     };
00149 
00150 
00151     bool operator<(const UIDToFAPair &lhs, const UIDToFAPair &rhs)
00152     {
00153         if (lhs.m_file_assoc && rhs.m_file_assoc)
00154             return QString::localeAwareCompare(lhs.m_file_assoc->GetExtension(),
00155                     rhs.m_file_assoc->GetExtension()) < 0;
00156 
00157         return rhs.m_file_assoc;
00158     }
00159 }
00160 
00162 
00163 class FileAssocDialogPrivate
00164 {
00165   public:
00166     typedef std::vector<UIDToFAPair> UIReadyList_type;
00167 
00168   public:
00169     FileAssocDialogPrivate() : m_nextFAID(0), m_selectionOverride(0)
00170     {
00171         LoadFileAssociations();
00172     }
00173 
00174     ~FileAssocDialogPrivate()
00175     {
00176         for (FA_collection::iterator p = m_fileAssociations.begin();
00177                 p != m_fileAssociations.end(); ++p)
00178         {
00179             delete p->second;
00180         }
00181     }
00182 
00183    void SaveFileAssociations()
00184    {
00185         for (FA_collection::iterator p = m_fileAssociations.begin();
00186                 p != m_fileAssociations.end(); ++p)
00187         {
00188             p->second->CommitChanges();
00189         }
00190     }
00191 
00192     bool AddExtension(QString newExtension, UIDToFAPair::UID_type &new_id)
00193     {
00194         if (newExtension.length())
00195         {
00196             new_id = ++m_nextFAID;
00197             m_fileAssociations.insert(FA_collection::value_type(new_id,
00198                             new FileAssociationWrap(newExtension)));
00199             return true;
00200         }
00201 
00202         return false;
00203     }
00204 
00205     bool DeleteExtension(UIDToFAPair::UID_type uid)
00206     {
00207         FA_collection::iterator p = m_fileAssociations.find(uid);
00208         if (p != m_fileAssociations.end())
00209         {
00210             p->second->MarkForDeletion();
00211 
00212             return true;
00213         }
00214 
00215         return false;
00216     }
00217 
00218     // Returns a list sorted by extension
00219     UIReadyList_type GetUIReadyList()
00220     {
00221         UIReadyList_type ret;
00222         std::transform(m_fileAssociations.begin(), m_fileAssociations.end(),
00223                 std::back_inserter(ret), fa_col_ent_2_UIDFAPair());
00224         UIReadyList_type::iterator deleted = std::remove_if(ret.begin(),
00225                 ret.end(), test_fa_state<FileAssociationWrap::efsDELETE>());
00226 
00227         if (deleted != ret.end())
00228             ret.erase(deleted, ret.end());
00229 
00230         std::sort(ret.begin(), ret.end());
00231 
00232         return ret;
00233     }
00234 
00235     FileAssociationWrap *GetCurrentFA(MythUIButtonList *buttonList)
00236     {
00237         MythUIButtonListItem *item = buttonList->GetItemCurrent();
00238         if (item)
00239         {
00240             UIDToFAPair key = item->GetData().value<UIDToFAPair>();
00241             if (key.m_file_assoc)
00242             {
00243                 return key.m_file_assoc;
00244             }
00245         }
00246 
00247         return NULL;
00248     }
00249 
00250     void SetSelectionOverride(UIDToFAPair::UID_type new_sel)
00251     {
00252         m_selectionOverride = new_sel;
00253     }
00254 
00255     UIDToFAPair::UID_type GetSelectionOverride() const
00256     {
00257         return m_selectionOverride;
00258     }
00259 
00260   private:
00261     typedef std::map<UIDToFAPair::UID_type, FileAssociationWrap *>
00262             FA_collection;
00263 
00264   private:
00265     struct fa_col_ent_2_UIDFAPair
00266     {
00267         UIDToFAPair operator()(
00268                 const FileAssocDialogPrivate::FA_collection::value_type &from)
00269         {
00270             return UIDToFAPair(from.first, from.second);
00271         }
00272     };
00273 
00274     template <FileAssociationWrap::FA_State against>
00275     struct test_fa_state
00276     {
00277         bool operator()(const UIDToFAPair &item)
00278         {
00279             if (item.m_file_assoc && item.m_file_assoc->GetState() == against)
00280                 return true;
00281             return false;
00282         }
00283     };
00284 
00285     void LoadFileAssociations()
00286     {
00287         typedef std::vector<UIDToFAPair> tmp_fa_list;
00288 
00289         const FileAssociations::association_list &fa_list =
00290                 FileAssociations::getFileAssociation().getList();
00291         tmp_fa_list tmp_fa;
00292         tmp_fa.reserve(fa_list.size());
00293 
00294         for (FileAssociations::association_list::const_iterator p =
00295                 fa_list.begin(); p != fa_list.end(); ++p)
00296         {
00297             tmp_fa.push_back(UIDToFAPair(++m_nextFAID,
00298                             new FileAssociationWrap(*p)));
00299         }
00300 
00301         std::random_shuffle(tmp_fa.begin(), tmp_fa.end());
00302 
00303         for (tmp_fa_list::const_iterator p = tmp_fa.begin(); p != tmp_fa.end();
00304                 ++p)
00305         {
00306             m_fileAssociations.insert(FA_collection::value_type(p->m_uid,
00307                             p->m_file_assoc));
00308         }
00309 
00310         if (m_fileAssociations.empty())
00311         {
00312             LOG(VB_GENERAL, LOG_ERR,
00313                 QString("%1: Couldn't get any filetypes from your database.")
00314                     .arg(__FILE__));
00315         }
00316     }
00317 
00318   private:
00319     FA_collection m_fileAssociations;
00320     UIDToFAPair::UID_type m_nextFAID;
00321     UIDToFAPair::UID_type m_selectionOverride;
00322 };
00323 
00325 
00326 FileAssocDialog::FileAssocDialog(MythScreenStack *screenParent,
00327         const QString &lname) :
00328     MythScreenType(screenParent, lname), m_commandEdit(0),
00329     m_extensionList(0), m_defaultCheck(0), m_ignoreCheck(0), m_doneButton(0),
00330     m_newButton(0), m_deleteButton(0), m_private(new FileAssocDialogPrivate)
00331 {
00332 }
00333 
00334 FileAssocDialog::~FileAssocDialog()
00335 {
00336     delete m_private;
00337 }
00338 
00339 bool FileAssocDialog::Create()
00340 {
00341     if (!LoadWindowFromXML("video-ui.xml", "file_associations", this))
00342         return false;
00343 
00344     bool err = false;
00345     UIUtilE::Assign(this, m_extensionList, "extension_select", &err);
00346     UIUtilE::Assign(this, m_commandEdit, "command", &err);
00347     UIUtilE::Assign(this, m_ignoreCheck, "ignore_check", &err);
00348     UIUtilE::Assign(this, m_defaultCheck, "default_check", &err);
00349 
00350     UIUtilE::Assign(this, m_doneButton, "done_button", &err);
00351     UIUtilE::Assign(this, m_newButton, "new_button", &err);
00352     UIUtilE::Assign(this, m_deleteButton, "delete_button", &err);
00353 
00354     if (err)
00355     {
00356         LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'file_associations'");
00357         return false;
00358     }
00359 
00360     connect(m_extensionList, SIGNAL(itemSelected(MythUIButtonListItem *)),
00361             SLOT(OnFASelected(MythUIButtonListItem *)));
00362     connect(m_commandEdit, SIGNAL(valueChanged()),
00363             SLOT(OnPlayerCommandChanged()));
00364     connect(m_defaultCheck, SIGNAL(valueChanged()), SLOT(OnUseDefaltChanged()));
00365     connect(m_ignoreCheck, SIGNAL(valueChanged()), SLOT(OnIgnoreChanged()));
00366 
00367     connect(m_doneButton, SIGNAL(Clicked()), SLOT(OnDonePressed()));
00368     connect(m_newButton, SIGNAL(Clicked()),
00369             SLOT(OnNewExtensionPressed()));
00370     connect(m_deleteButton, SIGNAL(Clicked()), SLOT(OnDeletePressed()));
00371 
00372     m_extensionList->SetHelpText(tr("Select a file extension from this list "
00373                                     "to modify or delete its settings."));
00374     m_commandEdit->SetHelpText(tr("The command to use when playing this kind "
00375                                   "of file.  To use MythTV's Internal player, "
00376                                   "use \"Internal\" as the player.  For all other "
00377                                   "players, you can use %s to substitute the filename."));
00378     m_ignoreCheck->SetHelpText(tr("When checked, this will cause the file extension "
00379                                   "to be ignored in scans of your library."));
00380     m_defaultCheck->SetHelpText(tr("When checked, this will cause the global player "
00381                                    "settings to override this one."));
00382     m_doneButton->SetHelpText(tr("Save and exit this screen."));
00383     m_newButton->SetHelpText(tr("Create a new file extension."));
00384     m_deleteButton->SetHelpText(tr("Delete this file extension."));
00385 
00386     UpdateScreen();
00387 
00388     BuildFocusList();
00389 
00390     return true;
00391 }
00392 
00393 void FileAssocDialog::OnFASelected(MythUIButtonListItem *item)
00394 {
00395     (void) item;
00396     UpdateScreen();
00397 }
00398 
00399 void FileAssocDialog::OnUseDefaltChanged()
00400 {
00401     if (m_private->GetCurrentFA(m_extensionList))
00402         m_private->GetCurrentFA(m_extensionList)->
00403                 SetDefault(m_defaultCheck->GetBooleanCheckState());
00404 }
00405 
00406 void FileAssocDialog::OnIgnoreChanged()
00407 {
00408     if (m_private->GetCurrentFA(m_extensionList))
00409         m_private->GetCurrentFA(m_extensionList)->
00410                 SetIgnore(m_ignoreCheck->GetBooleanCheckState());
00411 }
00412 
00413 void FileAssocDialog::OnPlayerCommandChanged()
00414 {
00415     if (m_private->GetCurrentFA(m_extensionList))
00416         m_private->GetCurrentFA(m_extensionList)->
00417                 SetCommand(m_commandEdit->GetText());
00418 }
00419 
00420 void FileAssocDialog::OnDonePressed()
00421 {
00422     m_private->SaveFileAssociations();
00423     Close();
00424 }
00425 
00426 void FileAssocDialog::OnDeletePressed()
00427 {
00428     MythUIButtonListItem *item = m_extensionList->GetItemCurrent();
00429     if (item)
00430     {
00431         UIDToFAPair key = item->GetData().value<UIDToFAPair>();
00432         if (key.m_file_assoc && m_private->DeleteExtension(key.m_uid))
00433             delete item;
00434     }
00435 
00436     UpdateScreen();
00437 }
00438 
00439 void FileAssocDialog::OnNewExtensionPressed()
00440 {
00441     MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00442 
00443     QString message = tr("Enter the new extension:");
00444 
00445     MythTextInputDialog *newextdialog =
00446                                 new MythTextInputDialog(popupStack, message);
00447 
00448     if (newextdialog->Create())
00449         popupStack->AddScreen(newextdialog);
00450 
00451     connect(newextdialog, SIGNAL(haveResult(QString)),
00452             SLOT(OnNewExtensionComplete(QString)));
00453 }
00454 
00455 void FileAssocDialog::OnNewExtensionComplete(QString newExtension)
00456 {
00457     UIDToFAPair::UID_type new_sel = 0;
00458     if (m_private->AddExtension(newExtension, new_sel))
00459     {
00460         m_private->SetSelectionOverride(new_sel);
00461         UpdateScreen(true);
00462     }
00463 }
00464 
00465 void FileAssocDialog::UpdateScreen(bool useSelectionOverride /* = false*/)
00466 {
00467     BlockSignalsGuard bsg;
00468     bsg.Block(m_extensionList);
00469     bsg.Block(m_commandEdit);
00470     bsg.Block(m_defaultCheck);
00471     bsg.Block(m_ignoreCheck);
00472 
00473     FileAssocDialogPrivate::UIReadyList_type tmp_list =
00474             m_private->GetUIReadyList();
00475 
00476     if (tmp_list.empty())
00477     {
00478         m_extensionList->SetVisible(false);
00479         m_commandEdit->SetVisible(false);
00480         m_defaultCheck->SetVisible(false);
00481         m_ignoreCheck->SetVisible(false);
00482         m_deleteButton->SetVisible(false);
00483     }
00484     else
00485     {
00486         UIDToFAPair::UID_type selected_id = 0;
00487         MythUIButtonListItem *current_item = m_extensionList->GetItemCurrent();
00488         if (current_item)
00489         {
00490             UIDToFAPair key = current_item->GetData().value<UIDToFAPair>();
00491             if (key.m_file_assoc)
00492             {
00493                 selected_id = key.m_uid;
00494             }
00495         }
00496 
00497         if (useSelectionOverride)
00498             selected_id = m_private->GetSelectionOverride();
00499 
00500         m_extensionList->SetVisible(true);
00501         m_extensionList->Reset();
00502 
00503         for (FileAssocDialogPrivate::UIReadyList_type::iterator p =
00504                 tmp_list.begin(); p != tmp_list.end(); ++p)
00505         {
00506             if (p->m_file_assoc)
00507             {
00508                 MythUIButtonListItem *new_item =
00509                         new MythUIButtonListItem(m_extensionList,
00510                                 p->m_file_assoc->GetExtension(),
00511                                 QVariant::fromValue(*p));
00512                 if (selected_id && p->m_uid == selected_id)
00513                     m_extensionList->SetItemCurrent(new_item);
00514             }
00515         }
00516 
00517         current_item = m_extensionList->GetItemCurrent();
00518         if (current_item)
00519         {
00520             UIDToFAPair key = current_item->GetData().value<UIDToFAPair>();
00521             if (key.m_file_assoc)
00522             {
00523                 m_commandEdit->SetVisible(true);
00524                 m_commandEdit->SetText(key.m_file_assoc->GetCommand());
00525 
00526                 m_defaultCheck->SetVisible(true);
00527                 m_defaultCheck->SetCheckState(key.m_file_assoc->GetDefault() ?
00528                         MythUIStateType::Full : MythUIStateType::Off);
00529 
00530                 m_ignoreCheck->SetVisible(true);
00531                 m_ignoreCheck->SetCheckState(key.m_file_assoc->GetIgnore() ?
00532                         MythUIStateType::Full : MythUIStateType::Off);
00533 
00534                 m_deleteButton->SetVisible(true);
00535             }
00536         }
00537     }
00538 
00539     BuildFocusList();
00540 }
00541 
00542 Q_DECLARE_METATYPE(UIDToFAPair)
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends