|
MythTV
0.26-pre
|
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)
1.7.6.1