00001 #include <qapplication.h>
00002 #include <qstringlist.h>
00003 #include <qpixmap.h>
00004 #include <qdir.h>
00005 #include <qurloperator.h>
00006 #include <qfileinfo.h>
00007 #include <qprocess.h>
00008 #include <qpainter.h>
00009
00010 #include <unistd.h>
00011 #include <cstdlib>
00012
00013 #include <memory>
00014 #include <map>
00015 #include <list>
00016 #include <stack>
00017 #include <set>
00018 #include <cmath>
00019 #include <functional>
00020 #include <algorithm>
00021
00022 #include <mythtv/mythcontext.h>
00023 #include <mythtv/xmlparse.h>
00024 #include <mythtv/compat.h>
00025
00026 #include "globals.h"
00027 #include "videomanager.h"
00028 #include "videolist.h"
00029
00030 #include "videofilter.h"
00031 #include "metadata.h"
00032 #include "editmetadata.h"
00033 #include "videoutils.h"
00034 #include "metadatalistmanager.h"
00035
00036
00037 #define VB_DEBUG VB_IMPORTANT // A way to mark VERBOSE calls that probably
00038
00039
00040 namespace mythvideo_videomanager
00041 {
00042 class ListBehaviorManager
00043 {
00044 public:
00045 enum ListBehavior
00046 {
00047 lbNone = 0x0,
00048 lbScrollCenter = 0x1,
00049 lbWrapList = 0x2
00050 };
00051
00052
00053 struct const_iterator
00054 {
00055 const_iterator(unsigned int item_index_) :
00056 item_index(item_index_), m_window_index(0)
00057 {
00058 }
00059
00060 const_iterator(const const_iterator &other) :
00061 item_index(other.item_index),
00062 m_window_index(other.m_window_index)
00063 {
00064 }
00065
00066 unsigned int item_index;
00067
00068 const_iterator &operator++()
00069 {
00070 ++m_window_index;
00071 ++item_index;
00072 return *this;
00073 }
00074
00075 bool operator!=(const const_iterator &rhs)
00076 {
00077 return item_index != rhs.item_index;
00078 }
00079
00080 unsigned int operator*()
00081 {
00082 return m_window_index;
00083 }
00084
00085 private:
00086 unsigned int m_window_index;
00087 };
00088
00089 public:
00090 ListBehaviorManager(unsigned int window_size = 0,
00091 int behavior = lbNone,
00092 unsigned int item_count = 0) :
00093 m_item_count(item_count), m_item_index(0), m_skip_index(SKIP_MAX),
00094 m_window_size(window_size), m_window_start_index(0),
00095 m_window_display_count(0)
00096 {
00097 m_scroll_center = behavior & lbScrollCenter;
00098 m_wrap_list = behavior & lbWrapList;
00099 }
00100
00101 const_iterator begin()
00102 {
00103 return const_iterator(m_window_start_index);
00104 }
00105
00106 const_iterator end()
00107 {
00108 return const_iterator(m_window_start_index +
00109 m_window_display_count);
00110 }
00111
00112 void SetWindowSize(unsigned int window_size)
00113 {
00114 m_window_size = window_size;
00115 m_window_display_count = std::min(m_item_count, m_window_size);
00116 m_window_start_index = 0;
00117 Update();
00118 }
00119
00120 unsigned int GetWindowSize() const { return m_window_size; }
00121
00122 unsigned int GetWindowIndex() const
00123 {
00124 return m_item_index - m_window_start_index;
00125 }
00126
00127 bool ItemsAboveWindow() const
00128 {
00129 return m_window_start_index;
00130 }
00131
00132 bool ItemsBelowWindow() const
00133 {
00134 return m_window_start_index + m_window_display_count <
00135 m_item_count;
00136 }
00137
00138 void SetItemCount(unsigned int item_count)
00139 {
00140 m_item_count = item_count;
00141 m_window_display_count = std::min(m_item_count, m_window_size);
00142 m_item_index = bounded_index(m_item_index);
00143 m_window_start_index = 0;
00144 Update();
00145 }
00146
00147 unsigned int GetItemCount() const
00148 {
00149 return m_item_count;
00150 }
00151
00152 void SetItemIndex(unsigned int index)
00153 {
00154 m_item_index = bounded_index(index);
00155 Update();
00156 }
00157
00158 unsigned int GetItemIndex() const
00159 {
00160 return m_item_index;
00161 }
00162
00163 void SetSkipIndex(unsigned int skip = SKIP_MAX)
00164 {
00165 m_skip_index = skip;
00166 Update();
00167 }
00168
00169 void Up()
00170 {
00171 Update(-1);
00172 }
00173
00174 void Down()
00175 {
00176 Update(1);
00177 }
00178
00179 void PageUp()
00180 {
00181 Update(-m_window_size);
00182 }
00183
00184 void PageDown()
00185 {
00186 Update(m_window_size);
00187 }
00188
00189 private:
00190 void Update(int move_by = 0)
00191 {
00192 if (move_by && m_item_count)
00193 {
00194 const unsigned int last_item_index = m_item_count - 1;
00195 const bool is_negative = move_by < 0 &&
00196 m_item_index < static_cast<unsigned int>
00197 (std::abs(move_by));
00198 unsigned int after_move = 0;
00199 if (!is_negative)
00200 after_move = m_item_index + move_by;
00201
00202 if (m_skip_index != SKIP_MAX &&
00203 after_move == m_skip_index)
00204 {
00205 if (move_by < 0)
00206 {
00207 if (after_move)
00208 --after_move;
00209 else
00210 ++after_move;
00211 }
00212 else
00213 ++after_move;
00214 }
00215
00216 if (is_negative)
00217 {
00218 if (m_wrap_list && m_item_index == 0)
00219 m_item_index = last_item_index;
00220 else
00221 m_item_index = 0;
00222 }
00223 else if (after_move >= m_item_count)
00224 {
00225 if (m_wrap_list && m_item_index == last_item_index)
00226 m_item_index = 0;
00227 else
00228 m_item_index = last_item_index;
00229 }
00230 else
00231 m_item_index = after_move;
00232 }
00233
00234
00235 const unsigned int half_window_count =
00236 static_cast<unsigned int>(std::ceil(m_window_size / 2.0));
00237 const unsigned int sc_end_count =
00238 half_window_count > m_item_count ?
00239 0 : m_item_count - half_window_count;
00240 if (m_scroll_center &&
00241 m_item_index >= half_window_count &&
00242 m_item_index <= sc_end_count)
00243 {
00244 m_window_start_index = m_item_index - half_window_count;
00245 }
00246 else
00247 {
00248
00249
00250 const unsigned int end_window_index =
00251 m_window_start_index + m_window_display_count;
00252 if (m_item_index < m_window_start_index)
00253 m_window_start_index = m_item_index;
00254 else if (m_item_index >= end_window_index)
00255 {
00256 m_window_start_index =
00257 m_item_index >= m_window_display_count ?
00258 m_item_index + 1 - m_window_display_count : 0;
00259 }
00260 }
00261 }
00262
00263 unsigned int bounded_index(unsigned int index)
00264 {
00265 unsigned int ret = 0;
00266
00267 if (m_item_count)
00268 ret = index >= m_item_count ? m_item_count - 1 : index;
00269
00270 return ret;
00271 }
00272
00273 private:
00274 unsigned int m_item_count;
00275 unsigned int m_item_index;
00276 unsigned int m_skip_index;
00277
00278 unsigned int m_window_size;
00279 unsigned int m_window_start_index;
00280 unsigned int m_window_display_count;
00281
00282 bool m_scroll_center;
00283 bool m_wrap_list;
00284
00285 static const unsigned int SKIP_MAX = -1;
00286 };
00287
00288 const unsigned int ListBehaviorManager::SKIP_MAX;
00289
00290
00291
00292 enum DefaultContext
00293 {
00294 edcHidden = -100,
00295 edcAlwaysShown = -1,
00296 edcWaitContext = 1,
00297 edcSearchListContext = 2,
00298 edcManualUIDSearchContext = 3,
00299 edcManualTitleSearchContext = 4,
00300 edcNoVideoContext = 20
00301 };
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314 struct ContainerDoneEvent : public QCustomEvent
00315 {
00316 enum MyType { etContainerDone = 311976 };
00317 ContainerDoneEvent() : QCustomEvent(etContainerDone) {}
00318 };
00319
00320 class ContainerHandler : public QObject
00321 {
00322 Q_OBJECT
00323
00324 public:
00325 enum HandlerFlag
00326 {
00327 ehfCanTakeFocus = 0x1 << 1
00328 };
00329
00330 typedef MythThemedDialog ParentWindowType;
00331
00332 enum ExitType
00333 {
00334 etSuccess,
00335 etFailure
00336 };
00337
00338 public:
00339 ContainerHandler(QObject *oparent, ParentWindowType *pwt,
00340 XMLParse &theme, const QString &container_name,
00341 unsigned int flags, int context_override = edcAlwaysShown) :
00342 QObject(oparent),
00343 m_container(0), m_theme(&theme), m_pwt(pwt), m_done(false),
00344 m_container_name(container_name),
00345 m_flags(flags), m_exit_type(etFailure)
00346 {
00347 if (m_theme)
00348 {
00349 m_container = m_theme->GetSet(m_container_name);
00350 if (m_container)
00351 {
00352 m_rect = m_container->GetAreaRect();
00353 if (m_container->GetContext() == edcAlwaysShown &&
00354 context_override != edcAlwaysShown)
00355 {
00356 ForceContext(m_container, context_override);
00357 }
00358 }
00359 else
00360 {
00361 VERBOSE(VB_IMPORTANT,
00362 QString("MythVideo: VideoManager : Failed to "
00363 "get %1 object.").arg(m_container_name));
00364 }
00365 }
00366 }
00367
00368 ~ContainerHandler()
00369 {
00370 }
00371
00372 unsigned int SetFlags(unsigned int flags)
00373 {
00374 m_flags = flags;
00375 return m_flags;
00376 }
00377
00378 unsigned int GetFlags() const { return m_flags; }
00379
00380 ParentWindowType *GetParentWindow() { return m_pwt; }
00381
00382 const QString &GetName() const { return m_container_name; }
00383
00384 const QRect &GetRect() const { return m_rect; }
00385
00386
00387 virtual bool KeyPress(const QString &action)
00388 {
00389 if (action == "ESCAPE")
00390 {
00391 SetDone(true, etFailure);
00392 return true;
00393 }
00394 return false;
00395 }
00396
00397 virtual void OnGainFocus() {}
00398 virtual void OnLoseFocus() {}
00399
00400 int GetContext()
00401 {
00402 if (m_container)
00403 return m_container->GetContext();
00404 return edcAlwaysShown;
00405 }
00406
00407
00408
00409 void DoExit()
00410 {
00411 OnExit(m_exit_type);
00412 }
00413
00414 bool IsDone() const { return m_done; }
00415
00416 virtual void Invalidate()
00417 {
00418 Invalidate(m_rect);
00419 }
00420
00421 private:
00422 void ForceContext(LayerSet *on, int context)
00423 {
00424 on->SetContext(context);
00425
00426 typedef std::vector<UIType *> ui_types_list;
00427 ui_types_list *items = on->getAllTypes();
00428 if (items)
00429 {
00430 for (ui_types_list::iterator p = items->begin();
00431 p != items->end(); ++p)
00432 {
00433 (*p)->SetContext(context);
00434 }
00435 }
00436 }
00437
00438 protected:
00439 void Invalidate(const QRect &rect)
00440 {
00441 m_pwt->updateForeground(rect);
00442 }
00443
00444 void Success()
00445 {
00446 SetDone(true, etSuccess);
00447 }
00448
00449 void Failure()
00450 {
00451 SetDone(true, etFailure);
00452 }
00453
00454
00455 virtual void OnExit(ExitType et)
00456 {
00457 (void) et;
00458 }
00459
00460 void SetDone(bool done, ExitType et)
00461 {
00462 m_done = done;
00463 m_exit_type = et;
00464 SetFlags(0);
00465 qApp->postEvent(parent(), new ContainerDoneEvent());
00466 }
00467
00468 protected:
00469 LayerSet *m_container;
00470 XMLParse *m_theme;
00471
00472 private:
00473 ParentWindowType *m_pwt;
00474 bool m_done;
00475 QString m_container_name;
00476 unsigned int m_flags;
00477 QRect m_rect;
00478 ExitType m_exit_type;
00479 };
00480
00481 struct ContainerEvent
00482 {
00483 enum EventType { cetNone, cetKeyPress };
00484
00485 ContainerEvent(EventType event_type = cetNone) :
00486 m_handled(false), m_event_type(event_type) {}
00487
00488 virtual ~ContainerEvent() {}
00489
00490 EventType GetType() const { return m_event_type; }
00491
00492 virtual void Do(ContainerHandler *handler) = 0;
00493
00494 bool GetHandled() const { return m_handled; }
00495 void SetHandled(bool handled = true) { m_handled = handled; }
00496
00497 private:
00498 bool m_handled;
00499 EventType m_event_type;
00500 };
00501
00502 struct CEKeyPress : public ContainerEvent
00503 {
00504 CEKeyPress(const QString &action) :
00505 ContainerEvent(ContainerEvent::cetKeyPress), m_action(action)
00506 {
00507 }
00508
00509 void Do(ContainerHandler *handler)
00510 {
00511 SetHandled(handler->KeyPress(m_action));
00512 }
00513
00514 private:
00515 CEKeyPress(const CEKeyPress &rhs);
00516
00517 private:
00518 QString m_action;
00519 };
00520
00521
00522
00523
00524 template <typename HandlerType, typename DialogType>
00525 class ContainerDispatch
00526 {
00527 public:
00528 ContainerDispatch(QObject *event_dest, DialogType *parent_dialog) :
00529 m_event_dest(event_dest), m_parent_dialog(parent_dialog), m_focus(0)
00530 {
00531 }
00532
00533 void push(HandlerType *handler)
00534 {
00535 m_handlers.push_back(handler);
00536 attach_handler(handler);
00537 }
00538
00539 HandlerType *pop()
00540 {
00541 HandlerType *ret = 0;
00542 if (m_handlers.size())
00543 {
00544 ret = m_handlers.back();
00545 m_handlers.pop_back();
00546 detach_handler(ret, true);
00547 }
00548
00549 return ret;
00550 }
00551
00552 bool DispatchEvent(ContainerEvent &event)
00553 {
00554 if (m_handlers.size())
00555 {
00556 bool do_dispatch = false;
00557 HandlerType *handler = m_handlers.back();
00558 switch (event.GetType())
00559 {
00560 case ContainerEvent::cetNone:
00561 {
00562 do_dispatch = true;
00563 break;
00564 }
00565 case ContainerEvent::cetKeyPress:
00566 {
00567 handler = GetFocusedContainer();
00568 if (handler && (handler->GetFlags() &
00569 HandlerType::ehfCanTakeFocus))
00570 {
00571 do_dispatch = true;
00572 }
00573 break;
00574 }
00575 }
00576
00577 if (do_dispatch)
00578 {
00579 event.Do(handler);
00580 }
00581 }
00582
00583 return event.GetHandled();
00584 }
00585
00586 void ProcessDone()
00587 {
00588 do_container_cleanup();
00589 }
00590
00591 private:
00592 HandlerType *GetFocusedContainer()
00593 {
00594 return m_focus;
00595 }
00596
00597 void attach_handler(HandlerType *handler)
00598 {
00599 if (m_parent_dialog->getContext() != handler->GetContext())
00600 {
00601 m_parent_dialog->setContext(handler->GetContext());
00602 m_parent_dialog->buildFocusList();
00603 }
00604
00605 HandlerType *next_focus = get_next_focus();
00606 if (m_focus && next_focus != m_focus)
00607 {
00608 m_focus->OnLoseFocus();
00609 }
00610 if (next_focus && next_focus != m_focus)
00611 {
00612 m_focus = next_focus;
00613 m_focus->OnGainFocus();
00614 }
00615
00616 handler->Invalidate();
00617 }
00618
00619 void detach_handler(HandlerType *handler)
00620 {
00621 if (handler == m_focus)
00622 {
00623 handler->OnLoseFocus();
00624 m_parent_dialog->buildFocusList();
00625 m_focus = get_next_focus();
00626 if (m_focus)
00627 m_focus->OnGainFocus();
00628 }
00629 }
00630
00631 HandlerType *get_next_focus()
00632 {
00633 HandlerType *ret = 0;
00634 if (m_handlers.size())
00635 {
00636 for (typename handlers::reverse_iterator p =
00637 m_handlers.rbegin(); p != m_handlers.rend(); ++p)
00638 {
00639 if ((*p)->GetFlags() & ContainerHandler::ehfCanTakeFocus)
00640 {
00641 ret = *p;
00642 break;
00643 }
00644 }
00645 }
00646
00647 return ret;
00648 }
00649
00650 void do_container_cleanup()
00651 {
00652 for (typename handlers::iterator p = m_handlers.begin();
00653 p != m_handlers.end();)
00654 {
00655
00656
00657 if ((*p)->IsDone())
00658 {
00659 HandlerType *ht = get_next_focus();
00660 int next_context = ht ? ht->GetContext() : edcAlwaysShown;
00661 if (m_parent_dialog->getContext() != next_context)
00662 {
00663 m_parent_dialog->setContext(next_context);
00664 }
00665
00666 detach_handler(*p);
00667
00668 (*p)->DoExit();
00669 (*p)->Invalidate();
00670 (*p)->deleteLater();
00671 p = m_handlers.erase(p);
00672 }
00673 else
00674 ++p;
00675 }
00676 }
00677
00678 private:
00679 typedef std::list<HandlerType *> handlers;
00680
00681 private:
00682 QObject *m_event_dest;
00683 DialogType *m_parent_dialog;
00684 handlers m_handlers;
00685 HandlerType *m_focus;
00686 };
00687
00689
00691
00692 class SearchListHandler : public ContainerHandler
00693 {
00694 Q_OBJECT
00695
00696 signals:
00697 void SigItemSelected(const QString &uid, const QString &title);
00698 void SigCancel();
00699 void SigReset();
00700 void SigManual();
00701 void SigManualTitle();
00702
00703 public:
00704
00705 typedef std::vector<std::pair<QString, QString> > item_list;
00706
00707 public:
00708 SearchListHandler(QObject *oparent, ParentWindowType *pwt,
00709 XMLParse &theme, const item_list &items,
00710 bool has_manual_title) :
00711 ContainerHandler(oparent, pwt, theme, "moviesel", ehfCanTakeFocus,
00712 edcSearchListContext),
00713 m_item_list(items), m_list(0)
00714 {
00715 const int initial_size = m_item_list.size();
00716
00717 if (initial_size)
00718 m_item_list.push_back(item_list::value_type("", ""));
00719
00720 m_item_list.push_back(item_list::value_type(Action_Manual,
00721 QObject::tr("Manually Enter Video #")));
00722 if (has_manual_title)
00723 m_item_list.push_back(item_list::value_type(Action_Manual_Title,
00724 QObject::tr("Manually Enter Video Title")));
00725 m_item_list.push_back(item_list::value_type(Action_Reset,
00726 QObject::tr("Reset Entry")));
00727 m_item_list.push_back(item_list::value_type(Action_Cancel,
00728 QObject::tr("Cancel")));
00729
00730 if (m_container)
00731 {
00732 m_list = dynamic_cast<UIListType *>
00733 (m_container->GetType("listing"));
00734 if (m_list)
00735 {
00736 m_list_behave.SetWindowSize(m_list->GetItems());
00737 m_list_behave.SetItemCount(m_item_list.size());
00738
00739 if (initial_size)
00740 m_list_behave.SetSkipIndex(initial_size);
00741 UpdateContents();
00742 }
00743 }
00744 }
00745
00746 bool KeyPress(const QString &action)
00747 {
00748 bool ret = true;
00749 if (action == "SELECT")
00750 {
00751 Success();
00752 }
00753 else if (action == "UP")
00754 {
00755 m_list_behave.Up();
00756 UpdateContents();
00757 }
00758 else if (action == "DOWN")
00759 {
00760 m_list_behave.Down();
00761 UpdateContents();
00762 }
00763 else if (action == "PAGEUP")
00764 {
00765 m_list_behave.PageUp();
00766 UpdateContents();
00767 }
00768 else if (action == "PAGEDOWN")
00769 {
00770 m_list_behave.PageDown();
00771 UpdateContents();
00772 }
00773 else
00774 {
00775 ret = ContainerHandler::KeyPress(action);
00776 }
00777
00778 return ret;
00779 }
00780
00781 void OnGainFocus()
00782 {
00783 if (m_list)
00784 {
00785 GetParentWindow()->setCurrentFocusWidget(m_list);
00786 m_list->SetActive(true);
00787 }
00788 }
00789
00790 void OnLoseFocus()
00791 {
00792 if (m_list)
00793 {
00794 m_list->SetActive(false);
00795 }
00796 }
00797
00798 private:
00799 void UpdateContents()
00800 {
00801 if (m_list)
00802 {
00803 m_list->ResetList();
00804 m_list->SetActive(true);
00805
00806 for (ListBehaviorManager::const_iterator p =
00807 m_list_behave.begin(); p != m_list_behave.end(); ++p)
00808 {
00809 m_list->SetItemText(*p, 1,
00810 m_item_list.at(p.item_index).second);
00811 }
00812
00813 m_list->SetItemCurrent(m_list_behave.GetWindowIndex());
00814 m_list->SetDownArrow(m_list_behave.ItemsBelowWindow());
00815 m_list->SetUpArrow(m_list_behave.ItemsAboveWindow());
00816 m_list->refresh();
00817 }
00818 }
00819
00820 void OnExit(ExitType et)
00821 {
00822 if (et == etSuccess)
00823 {
00824 const item_list::value_type sel_item =
00825 m_item_list.at(m_list_behave.GetItemIndex());
00826
00827 if (sel_item.first == Action_Manual)
00828 emit SigManual();
00829 else if (sel_item.first == Action_Manual_Title)
00830 emit SigManualTitle();
00831 else if (sel_item.first == Action_Reset)
00832 emit SigReset();
00833 else if (sel_item.first == Action_Cancel)
00834 emit SigCancel();
00835 else
00836 emit SigItemSelected(sel_item.first, sel_item.second);
00837 }
00838 else
00839 {
00840 emit SigCancel();
00841 }
00842
00843 ContainerHandler::OnExit(et);
00844 }
00845
00846 private:
00847 ListBehaviorManager m_list_behave;
00848 item_list m_item_list;
00849 UIListType *m_list;
00850
00851 static const QString Action_Cancel;
00852 static const QString Action_Manual;
00853 static const QString Action_Manual_Title;
00854 static const QString Action_Reset;
00855 };
00856
00857 const QString SearchListHandler::Action_Cancel("cancel");
00858 const QString SearchListHandler::Action_Manual("manual");
00859 const QString SearchListHandler::Action_Manual_Title("manual_title");
00860 const QString SearchListHandler::Action_Reset("reset");
00861
00862 class RemoteEditKeyFilter : public QObject
00863 {
00864 Q_OBJECT
00865
00866 signals:
00867 void SigSelect();
00868 void SigCancel();
00869
00870 public:
00871 enum FilerBehavior { efbNumbersOnly = 1 };
00872
00873 public:
00874 RemoteEditKeyFilter(QObject *oparent, unsigned int flags = 0) :
00875 QObject(oparent), m_flags(flags) {}
00876
00877 protected:
00878 bool eventFilter(QObject *dest, QEvent *levent)
00879 {
00880
00881 bool filtered = false;
00882
00883 if (levent->type() == QEvent::KeyPress)
00884 {
00885 QKeyEvent *kp = dynamic_cast<QKeyEvent *>(levent);
00886 switch (kp->key())
00887 {
00888 case Qt::Key_Return:
00889 case Qt::Key_Enter:
00890 {
00891 emit SigSelect();
00892 filtered = true;
00893 break;
00894 }
00895
00896 case Qt::Key_Up:
00897 case Qt::Key_Down:
00898 {
00899 filtered = true;
00900 break;
00901 }
00902 case Qt::Key_Escape:
00903 {
00904 filtered = true;
00905 emit SigCancel();
00906 break;
00907 }
00908 }
00909
00910 if (!filtered && (m_flags & efbNumbersOnly))
00911 {
00912 if (kp->key() != Qt::Key_Delete &&
00913 kp->key() != Qt::Key_Backspace &&
00914 kp->text().length())
00915 {
00916 filtered = true;
00917 MythRemoteLineEdit *mrle =
00918 dynamic_cast<MythRemoteLineEdit *>(dest);
00919 bool converted = false;
00920 unsigned int num = kp->text().toUInt(&converted);
00921 if (converted && mrle)
00922 {
00923 mrle->insert(QString::number(num));
00924 }
00925 }
00926 }
00927 }
00928
00929 return filtered;
00930 }
00931
00932 private:
00933 unsigned int m_flags;
00934 };
00935
00936 class ManualSearchUIDHandler : public ContainerHandler
00937 {
00938
00939
00940
00941 Q_OBJECT
00942
00943 signals:
00944 void SigTextChanged(const QString &uid);
00945
00946 public:
00947 ManualSearchUIDHandler(QObject *oparent, ParentWindowType *pwt,
00948 XMLParse &theme) :
00949 ContainerHandler(oparent, pwt, theme, "enterimdb", ehfCanTakeFocus,
00950 edcManualUIDSearchContext)
00951 {
00952 if (m_container)
00953 {
00954 m_edit = dynamic_cast<UIRemoteEditType *>
00955 (m_container->GetType("numhold"));
00956 if (m_edit)
00957 {
00958 QWidget *edit_control = m_edit->getEdit();
00959
00960 if (!edit_control)
00961 {
00962 m_edit->createEdit(GetParentWindow());
00963 edit_control = m_edit->getEdit();
00964 }
00965 else
00966 {
00967
00968 m_edit->setText("");
00969 m_edit->show();
00970 }
00971
00972 m_key_filter = new RemoteEditKeyFilter(this,
00973 RemoteEditKeyFilter::efbNumbersOnly);
00974 connect(m_key_filter, SIGNAL(SigSelect()),
00975 SLOT(OnEditSelect()));
00976 connect(m_key_filter, SIGNAL(SigCancel()),
00977 SLOT(OnEditCancel()));
00978
00979 if (edit_control)
00980 {
00981 edit_control->installEventFilter(m_key_filter);
00982 }
00983
00984 connect(m_edit, SIGNAL(textChanged(QString)),
00985 SLOT(OnTextChange(QString)));
00986 }
00987 }
00988 }
00989
00990 private:
00991 void UpdateContents()
00992 {
00993
00994 }
00995
00996 void OnExit(ExitType et)
00997 {
00998 if (m_edit)
00999 {
01000 m_edit->hide();
01001 QWidget *edit_control = m_edit->getEdit();
01002 if (edit_control)
01003 edit_control->removeEventFilter(m_key_filter);
01004 }
01005
01006 if (et == etSuccess)
01007 {
01008 emit SigTextChanged(m_number);
01009 }
01010 }
01011
01012 void OnGainFocus()
01013 {
01014 if (m_edit)
01015 {
01016 GetParentWindow()->setCurrentFocusWidget(m_edit);
01017 }
01018 }
01019
01020 private slots:
01021 void OnTextChange(QString text)
01022 {
01023 m_number = text;
01024 }
01025
01026 void OnEditSelect()
01027 {
01028 Success();
01029 }
01030
01031 void OnEditCancel()
01032 {
01033 Failure();
01034 }
01035
01036 private:
01037 QString m_number;
01038 UIRemoteEditType *m_edit;
01039 RemoteEditKeyFilter *m_key_filter;
01040 };
01041
01042 class ManualSearchHandler : public ContainerHandler
01043 {
01044 Q_OBJECT
01045
01046 signals:
01047 void SigTextChanged(const QString &text);
01048
01049 public:
01050 ManualSearchHandler(QObject *oparent, ParentWindowType *pwt,
01051 XMLParse &theme) :
01052 ContainerHandler(oparent, pwt, theme, container_name,
01053 ehfCanTakeFocus, edcManualTitleSearchContext),
01054 m_edit(0), m_key_filter(0)
01055 {
01056 if (m_container)
01057 {
01058 m_edit = dynamic_cast<UIRemoteEditType *>
01059 (m_container->GetType("title"));
01060 if (m_edit)
01061 {
01062 QWidget *edit_control = m_edit->getEdit();
01063
01064 if (!edit_control)
01065 {
01066 m_edit->createEdit(GetParentWindow());
01067 edit_control = m_edit->getEdit();
01068 }
01069 else
01070 {
01071
01072 m_edit->setText("");
01073 m_edit->show();
01074 }
01075
01076 m_key_filter = new RemoteEditKeyFilter(this);
01077 connect(m_key_filter, SIGNAL(SigSelect()),
01078 SLOT(OnEditSelect()));
01079 connect(m_key_filter, SIGNAL(SigCancel()),
01080 SLOT(OnEditCancel()));
01081
01082 if (edit_control)
01083 {
01084 edit_control->installEventFilter(m_key_filter);
01085 }
01086
01087 connect(m_edit, SIGNAL(textChanged(QString)),
01088 SLOT(OnTextChange(QString)));
01089 }
01090 }
01091 }
01092
01093 static bool Exists(XMLParse *theme)
01094 {
01095 return theme->GetSet(container_name);
01096 }
01097
01098 bool KeyPress(const QString &action)
01099 {
01100 VERBOSE(VB_DEBUG, QString("in KeyPress mysteriously"));
01101 bool ret = ContainerHandler::KeyPress(action);
01102 return ret;
01103 }
01104
01105 void UpdateContents()
01106 {
01107
01108
01109 }
01110
01111 private slots:
01112 void OnTextChange(QString text)
01113 {
01114 m_title = text;
01115 }
01116
01117 void OnEditSelect()
01118 {
01119 Success();
01120 }
01121
01122 void OnEditCancel()
01123 {
01124 Failure();
01125 }
01126
01127 private:
01128 void OnExit(ExitType et)
01129 {
01130 if (m_edit)
01131 {
01132 m_edit->hide();
01133 QWidget *edit_control = m_edit->getEdit();
01134 if (edit_control)
01135 edit_control->removeEventFilter(m_key_filter);
01136 }
01137
01138 if (et == etSuccess)
01139 {
01140 emit SigTextChanged(m_title);
01141 }
01142 }
01143
01144 void OnGainFocus()
01145 {
01146 if (m_edit)
01147 {
01148 GetParentWindow()->setCurrentFocusWidget(m_edit);
01149 }
01150 }
01151
01152 private:
01153 QString m_title;
01154 UIRemoteEditType *m_edit;
01155 static const QString container_name;
01156 RemoteEditKeyFilter *m_key_filter;
01157 };
01158
01159 const QString ManualSearchHandler::container_name("entersearchtitle");
01160
01161 class InfoHandler : public ContainerHandler
01162 {
01163 Q_OBJECT
01164
01165 public:
01166 struct CurrentInfoItemGetter
01167 {
01168 virtual const Metadata *GetItem() = 0;
01169 virtual ~CurrentInfoItemGetter() {}
01170 };
01171
01172 public:
01173 InfoHandler(QObject *oparent, ParentWindowType *pwt, XMLParse &theme,
01174 CurrentInfoItemGetter *item_get, const QString &art_dir) :
01175 ContainerHandler(oparent, pwt, theme, "info", 0),
01176 m_art_dir(art_dir), m_item_get(item_get)
01177 {
01178 m_norec_container = m_theme->GetSet("novideos_info");
01179 if (m_norec_container &&
01180 m_norec_container->GetContext() == edcAlwaysShown)
01181 m_norec_container->SetContext(edcNoVideoContext);
01182 }
01183
01184 void Update()
01185 {
01186 UpdateContents();
01187 Invalidate();
01188 }
01189
01190 private:
01191 void UpdateContents()
01192 {
01193 const Metadata *item = m_item_get->GetItem();
01194
01195 if (m_container && m_norec_container)
01196 {
01197 m_container->SetContext(item ? edcAlwaysShown : edcHidden);
01198 m_norec_container->SetContext(item ? edcHidden : edcAlwaysShown);
01199 }
01200
01201 if (item && m_container)
01202 {
01203 checkedSetText(m_container, "title", item->Title());
01204 checkedSetText(m_container, "filename",
01205 item->getFilenameNoPrefix());
01206 checkedSetText(m_container, "video_player",
01207 Metadata::getPlayer(item));
01208 checkedSetText(m_container, "director", item->Director());
01209 checkedSetText(m_container, "cast", GetCast(*item));
01210 checkedSetText(m_container, "plot", item->Plot());
01211 checkedSetText(m_container, "rating", item->Rating());
01212 checkedSetText(m_container, "inetref", item->InetRef());
01213 checkedSetText(m_container, "year",
01214 getDisplayYear(item->Year()));
01215 checkedSetText(m_container, "userrating",
01216 getDisplayUserRating(item->UserRating()));
01217 checkedSetText(m_container, "length",
01218 getDisplayLength(item->Length()));
01219
01220 QString coverfile = item->CoverFile();
01221 coverfile.remove(m_art_dir + "/");
01222 checkedSetText(m_container, "coverfile", coverfile);
01223
01224 checkedSetText(m_container, "child_id",
01225 QString::number(item->ChildID()));
01226 checkedSetText(m_container, "browseable",
01227 getDisplayBrowse(item->Browse()));
01228 checkedSetText(m_container, "category", item->Category());
01229 checkedSetText(m_container, "level",
01230 QString::number(item->ShowLevel()));
01231 }
01232 }
01233
01234 protected:
01235 void Invalidate()
01236 {
01237 QRect ir;
01238
01239 if (m_container && m_container->GetContext() == edcAlwaysShown)
01240 ir |= m_container->GetAreaRect();
01241
01242 if (m_norec_container && m_norec_container->GetContext() ==
01243 edcAlwaysShown)
01244 ir |= m_norec_container->GetAreaRect();
01245
01246 if (ir.isValid())
01247 ContainerHandler::Invalidate(ir);
01248 }
01249
01250 private:
01251 QString m_art_dir;
01252 CurrentInfoItemGetter *m_item_get;
01253 LayerSet *m_norec_container;
01254 };
01255
01256
01257 class ListHandler : public ContainerHandler
01258 {
01259 Q_OBJECT
01260
01261 signals:
01262 void SigSelectionChanged();
01263 void SigItemEdit();
01264 void SigItemDelete();
01265 void SigItemToggleBrowseable();
01266 void SigItemChangeParental(int);
01267 void SigDoFilter();
01268 void SigDoMenu();
01269
01270 void ListHandlerExit();
01271
01272 public:
01273 ListHandler(QObject *oparent, ParentWindowType *pwt, XMLParse &theme,
01274 VideoList *video_list) :
01275 ContainerHandler(oparent, pwt, theme, "selector", ehfCanTakeFocus),
01276 m_list_behave(0, ListBehaviorManager::lbScrollCenter |
01277 ListBehaviorManager::lbWrapList),
01278 m_video_list(video_list)
01279 {
01280 m_list =
01281 dynamic_cast<UIListType *>(m_container->GetType("listing"));
01282 if (m_list)
01283 m_list_behave.SetWindowSize(m_list->GetItems());
01284 SetSelectedItem(0);
01285 }
01286
01287 void SetSelectedItem(unsigned int index)
01288 {
01289 m_list_behave.SetItemIndex(index);
01290 UpdateContents();
01291 emit SigSelectionChanged();
01292 }
01293
01294
01295 void OnListChanged()
01296 {
01297 m_list_behave.SetItemCount(m_video_list->count());
01298 UpdateContents();
01299 emit SigSelectionChanged();
01300 }
01301
01302 Metadata *GetCurrentItem()
01303 {
01304 return m_video_list->
01305 getVideoListMetadata(m_list_behave.GetItemIndex());
01306 }
01307
01308 bool KeyPress(const QString &action)
01309 {
01310 bool ret = true;
01311 const unsigned int curindex = m_list_behave.GetItemIndex();
01312
01313 if (action == "SELECT")
01314 emit SigItemEdit();
01315 else if (action == "UP")
01316 m_list_behave.Up();
01317 else if (action == "DOWN")
01318 m_list_behave.Down();
01319 else if (action == "PAGEUP")
01320 m_list_behave.PageUp();
01321 else if (action == "PAGEDOWN")
01322 m_list_behave.PageDown();
01323 else if (action == "DELETE")
01324 emit SigItemDelete();
01325 else if (action == "BROWSE")
01326 emit SigItemToggleBrowseable();
01327 else if (action == "INCPARENT")
01328 emit SigItemChangeParental(1);
01329 else if (action == "DECPARENT")
01330 emit SigItemChangeParental(-1);
01331 else if (action == "FILTER")
01332 emit SigDoFilter();
01333 else if ((action == "INFO") || action == "MENU")
01334 emit SigDoMenu();
01335 else if (action == "LEFT" || action == "ESCAPE")
01336 Success();
01337 else if (action == "RIGHT")
01338 emit SigDoMenu();
01339 else
01340 ret = false;
01341
01342
01343 if (curindex != m_list_behave.GetItemIndex())
01344 {
01345 UpdateContents();
01346 emit SigSelectionChanged();
01347 }
01348
01349 return ret;
01350 }
01351
01352 void Update()
01353 {
01354 UpdateContents();
01355 }
01356
01357 void OnGainFocus()
01358 {
01359 if (m_list)
01360 {
01361 GetParentWindow()->setCurrentFocusWidget(m_list);
01362 m_list->SetActive(true);
01363 }
01364 }
01365
01366 void OnLoseFocus()
01367 {
01368 if (m_list)
01369 {
01370 m_list->SetActive(false);
01371 }
01372 }
01373
01374 private:
01375 void UpdateContents()
01376 {
01377 if (m_list)
01378 {
01379 m_list->ResetList();
01380 m_list->SetActive(true);
01381
01382 for (ListBehaviorManager::const_iterator p =
01383 m_list_behave.begin(); p != m_list_behave.end(); ++p)
01384 {
01385 Metadata *meta =
01386 m_video_list->getVideoListMetadata(p.item_index);
01387
01388 QString title = meta->Title();
01389 QString filename = meta->Filename();
01390 if (0 == title.compare("title"))
01391 {
01392 title = filename.section('/', -1);
01393 if (!gContext->GetNumSetting("ShowFileExtensions"))
01394 title = title.section('.',0,-2);
01395 }
01396
01397 m_list->SetItemText(*p, 1, title);
01398 m_list->SetItemText(*p, 2, meta->Director());
01399 m_list->SetItemText(*p, 3, getDisplayYear(meta->Year()));
01400 }
01401
01402 m_list->SetItemCurrent(m_list_behave.GetWindowIndex());
01403 m_list->SetDownArrow(m_list_behave.ItemsBelowWindow());
01404 m_list->SetUpArrow(m_list_behave.ItemsAboveWindow());
01405 m_list->refresh();
01406 }
01407 }
01408
01409 void OnExit(ExitType et)
01410 {
01411 (void) et;
01412 emit ListHandlerExit();
01413 }
01414
01415 private:
01416 ListBehaviorManager m_list_behave;
01417 VideoList *m_video_list;
01418 UIListType *m_list;
01419 };
01420
01421 class WaitBackgroundHandler : public ContainerHandler
01422 {
01423 Q_OBJECT
01424
01425 public:
01426 WaitBackgroundHandler(QObject *oparent, ParentWindowType *pwt,
01427 XMLParse &theme) :
01428 ContainerHandler(oparent, pwt, theme, "inetwait", ehfCanTakeFocus,
01429 edcWaitContext)
01430 {
01431 }
01432
01433 void EnterMessage(const QString &message)
01434 {
01435 m_message.push(message);
01436 UpdateContents();
01437 }
01438
01439 bool LeaveMessage()
01440 {
01441 m_message.pop();
01442 bool more = m_message.size();
01443 if (more)
01444 {
01445 UpdateContents();
01446 }
01447
01448 return more;
01449 }
01450
01451 void Close()
01452 {
01453 Success();
01454 }
01455
01456 bool KeyPress(const QString &action)
01457 {
01458 (void) action;
01459 return true;
01460 }
01461
01462 private:
01463 void UpdateContents()
01464 {
01465
01466 if (m_message.size())
01467 {
01468 checkedSetText(m_container, "title", m_message.top());
01469 }
01470 }
01471
01472 private:
01473 std::stack<QString> m_message;
01474 };
01475
01476 class ExecuteExternalCommand : public QObject
01477 {
01478 Q_OBJECT
01479
01480 protected:
01481 ExecuteExternalCommand(QObject *oparent) : QObject(oparent),
01482 m_process(this), m_purpose(QObject::tr("Command"))
01483 {
01484 connect(&m_process, SIGNAL(readyReadStdout()),
01485 SLOT(OnReadReadyStdout()));
01486 connect(&m_process, SIGNAL(readyReadStderr()),
01487 SLOT(OnReadReadyStderr()));
01488 connect(&m_process, SIGNAL(processExited()),
01489 SLOT(OnProcessExit()));
01490 }
01491
01492 ~ExecuteExternalCommand()
01493 {
01494 }
01495
01496 void StartRun(const QString &command, const QStringList &args,
01497 const QString &purpose)
01498 {
01499 m_purpose = purpose;
01500
01501
01502 QStringList split_args = QStringList::split(' ', command);
01503 split_args += args;
01504
01505 m_process.clearArguments();
01506 m_process.setArguments(split_args);
01507
01508 VERBOSE(VB_GENERAL, QString("%1: Executing '%2'").arg(purpose)
01509 .arg(split_args.join(" ")));
01510
01511 m_raw_cmd = split_args[0];
01512 QFileInfo fi(m_raw_cmd);
01513
01514 QString err_msg;
01515
01516 if (!fi.exists())
01517 {
01518 err_msg = QString("\"%1\" failed: does not exist")
01519 .arg(m_raw_cmd);
01520 }
01521 else if (!fi.isExecutable())
01522 {
01523 err_msg = QString("\"%1\" failed: not executable")
01524 .arg(m_raw_cmd);
01525 }
01526 else if (!m_process.start())
01527 {
01528 err_msg = QString("\"%1\" failed: Could not start process")
01529 .arg(m_raw_cmd);
01530 }
01531
01532 if (err_msg.length())
01533 {
01534 ShowError(err_msg);
01535 }
01536 }
01537
01538 virtual void OnExecDone(bool normal_exit, const QStringList &out,
01539 const QStringList &err) = 0;
01540
01541 private slots:
01542 void OnReadReadyStdout()
01543 {
01544 QByteArray buf = m_process.readStdout();
01545 m_std_out += QString::fromUtf8(buf.data(), buf.size());
01546 }
01547
01548 void OnReadReadyStderr()
01549 {
01550 QByteArray buf = m_process.readStderr();
01551 m_std_error += QString::fromUtf8(buf.data(), buf.size());
01552 }
01553
01554 void OnProcessExit()
01555 {
01556 if (!m_process.normalExit())
01557 {
01558 ShowError(QString("\"%1\" failed: Process exited abnormally")
01559 .arg(m_raw_cmd));
01560 }
01561
01562 if (m_std_error.length())
01563 {
01564 ShowError(m_std_error);
01565 }
01566
01567 QStringList std_out = QStringList::split("\n", m_std_out);
01568 for (QStringList::iterator p = std_out.begin();
01569 p != std_out.end(); )
01570 {
01571 QString check = (*p).stripWhiteSpace();
01572 if (check.at(0) == '#' || !check.length())
01573 {
01574 p = std_out.erase(p);
01575 }
01576 else
01577 ++p;
01578 }
01579
01580 VERBOSE(VB_IMPORTANT, m_std_out);
01581
01582 OnExecDone(m_process.normalExit(), std_out,
01583 QStringList::split("\n", m_std_error));
01584 }
01585
01586 private:
01587 void ShowError(const QString &error_msg)
01588 {
01589 VERBOSE(VB_IMPORTANT, error_msg);
01590
01591 MythPopupBox::showOkPopup(gContext->GetMainWindow(),
01592 QString(QObject::tr("%1 failed")).arg(m_purpose),
01593 QString(QObject::tr("%1\n\nCheck VideoManager Settings"))
01594 .arg(error_msg));
01595 }
01596
01597 private:
01598 QString m_std_error;
01599 QString m_std_out;
01600 QProcess m_process;
01601 QString m_purpose;
01602 QString m_raw_cmd;
01603 };
01604
01605
01606 class VideoTitleSearch : public ExecuteExternalCommand
01607 {
01608 Q_OBJECT
01609
01610 signals:
01611 void SigSearchResults(bool normal_exit,
01612 const SearchListHandler::item_list &items,
01613 Metadata *item);
01614
01615 public:
01616 VideoTitleSearch(QObject *oparent) : ExecuteExternalCommand(oparent),
01617 m_item(0)
01618 {
01619 }
01620
01621 void Run(const QString &title, Metadata *item)
01622 {
01623 m_item = item;
01624
01625 QString def_cmd = QDir::cleanDirPath(QString("%1/%2")
01626 .arg(gContext->GetShareDir())
01627 .arg("mythvideo/scripts/imdb.pl -M tv=no;video=no"));
01628
01629 QString cmd = gContext->GetSetting("MovieListCommandLine", def_cmd);
01630
01631 QStringList args;
01632 args += title;
01633 StartRun(cmd, args, "Video Search");
01634 }
01635
01636 private:
01637 ~VideoTitleSearch() {}
01638
01639 void OnExecDone(bool normal_exit, const QStringList &out,
01640 const QStringList &err)
01641 {
01642 (void) err;
01643 typedef SearchListHandler::item_list item_list;
01644
01645 item_list results;
01646 if (normal_exit)
01647 {
01648 for (QStringList::const_iterator p = out.begin();
01649 p != out.end(); ++p)
01650 {
01651 results.push_back(item_list::value_type(
01652 (*p).section(':', 0, 0), (*p).section(':', 1)));
01653 }
01654 }
01655
01656 emit SigSearchResults(normal_exit, results, m_item);
01657 deleteLater();
01658 }
01659
01660 private:
01661 Metadata *m_item;
01662 };
01663
01664
01665 class VideoUIDSearch : public ExecuteExternalCommand
01666 {
01667 Q_OBJECT
01668
01669 signals:
01670 void SigSearchResults(bool normal_exit, const QStringList &results,
01671 Metadata *item, const QString &video_uid);
01672 public:
01673 VideoUIDSearch(QObject *oparent) : ExecuteExternalCommand(oparent),
01674 m_item(0)
01675 {
01676 }
01677
01678 void Run(const QString &video_uid, Metadata *item)
01679 {
01680 m_item = item;
01681 m_video_uid = video_uid;
01682
01683 const QString def_cmd = QDir::cleanDirPath(QString("%1/%2")
01684 .arg(gContext->GetShareDir())
01685 .arg("mythvideo/scripts/imdb.pl -D"));
01686 const QString cmd = gContext->GetSetting("MovieDataCommandLine",
01687 def_cmd);
01688
01689 StartRun(cmd, video_uid, "Video Data Query");
01690 }
01691
01692 private:
01693 ~VideoUIDSearch() {}
01694
01695 void OnExecDone(bool normal_exit, const QStringList &out,
01696 const QStringList &err)
01697 {
01698 (void) err;
01699 emit SigSearchResults(normal_exit, out, m_item, m_video_uid);
01700 deleteLater();
01701 }
01702
01703 private:
01704 Metadata *m_item;
01705 QString m_video_uid;
01706 };
01707
01708
01709 class VideoPosterSearch : public ExecuteExternalCommand
01710 {
01711 Q_OBJECT
01712
01713 signals:
01714 void SigPosterURL(const QString &url, Metadata *item);
01715
01716 public:
01717 VideoPosterSearch(QObject *oparent) : ExecuteExternalCommand(oparent),
01718 m_item(0)
01719 {
01720 }
01721
01722 void Run(const QString &video_uid, Metadata *item)
01723 {
01724 m_item = item;
01725
01726 const QString default_cmd =
01727 QDir::cleanDirPath(QString("%1/%2")
01728 .arg(gContext->GetShareDir())
01729 .arg("mythvideo/scripts/imdb.pl -P"));
01730 const QString cmd = gContext->GetSetting("MoviePosterCommandLine",
01731 default_cmd);
01732 StartRun(cmd, video_uid, "Poster Query");
01733 }
01734
01735 private:
01736 ~VideoPosterSearch() {}
01737
01738 void OnExecDone(bool normal_exit, const QStringList &out,
01739 const QStringList &err)
01740 {
01741 (void) err;
01742 QString url;
01743 if (normal_exit && out.size())
01744 {
01745 for (QStringList::const_iterator p = out.begin();
01746 p != out.end(); ++p)
01747 {
01748 if ((*p).length())
01749 {
01750 url = *p;
01751 break;
01752 }
01753 }
01754 }
01755
01756 emit SigPosterURL(url, m_item);
01757 deleteLater();
01758 }
01759
01760 private:
01761 Metadata *m_item;
01762 };
01763
01764
01765 class TimeoutSignalProxy : public QObject
01766 {
01767 Q_OBJECT
01768
01769 signals:
01770 void SigTimeout(const QString &url, Metadata *item);
01771
01772 public:
01773 TimeoutSignalProxy() : m_item(0), m_timer(this)
01774 {
01775 connect(&m_timer, SIGNAL(timeout()), SLOT(OnTimeout()));
01776 }
01777
01778 void start(int timeout, Metadata *item, const QString &url)
01779 {
01780 m_item = item;
01781 m_url = url;
01782 m_timer.start(timeout, true);
01783 }
01784
01785 void stop()
01786 {
01787 if (m_timer.isActive())
01788 m_timer.stop();
01789 }
01790
01791 private slots:
01792 void OnTimeout()
01793 {
01794 emit SigTimeout(m_url, m_item);
01795 }
01796
01797 private:
01798 Metadata *m_item;
01799 QString m_url;
01800 QTimer m_timer;
01801 };
01802
01803
01804 class URLOperationProxy : public QObject
01805 {
01806 Q_OBJECT
01807
01808 signals:
01809 void SigFinished(QNetworkOperation *op, Metadata *item);
01810
01811 public:
01812 URLOperationProxy() : m_item(0)
01813 {
01814 connect(&m_url_op, SIGNAL(finished(QNetworkOperation *)),
01815 SLOT(OnFinished(QNetworkOperation *)));
01816 }
01817
01818 void copy(const QString &uri, const QString &dest, Metadata *item)
01819 {
01820 m_item = item;
01821 m_url_op.copy(uri, dest, false, false);
01822 }
01823
01824 void stop()
01825 {
01826 m_url_op.stop();
01827 }
01828
01829 private slots:
01830 void OnFinished(QNetworkOperation *op)
01831 {
01832 emit SigFinished(op, m_item);
01833 }
01834
01835 private:
01836 Metadata *m_item;
01837 QUrlOperator m_url_op;
01838 };
01839
01840 class VideoManagerImp : public QObject
01841 {
01842 Q_OBJECT
01843
01844 private:
01845 struct CurrentItemGet : public InfoHandler::CurrentInfoItemGetter
01846 {
01847 CurrentItemGet() : m_list_handler(0) {}
01848
01849 const Metadata *GetItem()
01850 {
01851 if (m_list_handler)
01852 return m_list_handler->GetCurrentItem();
01853 return 0;
01854 }
01855
01856 void connect(ListHandler *handler)
01857 {
01858 m_list_handler = handler;
01859 }
01860
01861 void disconnect()
01862 {
01863 m_list_handler = 0;
01864 }
01865
01866 private:
01867 ListHandler *m_list_handler;
01868 };
01869
01870 typedef std::list<std::pair<QString, ParentalLevel::Level> >
01871 parental_level_map;
01872
01873 struct rating_to_pl_less :
01874 public std::binary_function<parental_level_map::value_type,
01875 parental_level_map::value_type, bool>
01876 {
01877 bool operator()(const parental_level_map::value_type &lhs,
01878 const parental_level_map::value_type &rhs) const
01879 {
01880 return lhs.first.length() < rhs.first.length();
01881 }
01882 };
01883
01884 public:
01885 VideoManagerImp(VideoManager *vm, XMLParse *theme, const QRect &area,
01886 VideoList *video_list) :
01887 m_event_dispatch(this, vm), m_vm(vm), m_theme(theme), m_area(area),
01888 m_video_list(video_list), m_info_handler(0), m_list_handler(0),
01889 m_popup(0), m_wait_background(0), m_has_manual_title_search(false)
01890 {
01891 m_art_dir = gContext->GetSetting("VideoArtworkDir");
01892
01893 if (gContext->
01894 GetNumSetting("mythvideo.ParentalLevelFromRating", 0))
01895 {
01896 for (ParentalLevel sl(ParentalLevel::plLowest);
01897 sl.GetLevel() <= ParentalLevel::plHigh && sl.good(); ++sl)
01898 {
01899 QStringList ratings = QStringList::split(':', gContext->
01900 GetSetting(QString("mythvideo.AutoR2PL%1")
01901 .arg(sl.GetLevel())));
01902
01903 for (QStringList::const_iterator p = ratings.begin();
01904 p != ratings.end(); ++p)
01905 {
01906 m_rating_to_pl.push_back(
01907 parental_level_map::value_type(*p, sl.GetLevel()));
01908 }
01909 }
01910 m_rating_to_pl.sort(std::not2(rating_to_pl_less()));
01911 }
01912
01913 m_info_handler = new InfoHandler(this, m_vm, *m_theme,
01914 &m_current_item_proxy, m_art_dir);
01915 m_list_handler = new ListHandler(this, m_vm, *m_theme, video_list);
01916
01917 m_current_item_proxy.connect(m_list_handler);
01918
01919 m_vm->connect(m_list_handler, SIGNAL(ListHandlerExit()),
01920 SLOT(ExitWin()));
01921
01922 m_event_dispatch.push(m_info_handler);
01923 m_event_dispatch.push(m_list_handler);
01924
01925 connect(m_list_handler, SIGNAL(SigSelectionChanged()),
01926 SLOT(OnListSelectionChange()));
01927
01928 connect(m_list_handler, SIGNAL(SigItemEdit()),
01929 SLOT(DoEditMetadata()));
01930 connect(m_list_handler, SIGNAL(SigItemDelete()),
01931 SLOT(DoRemoveVideo()));
01932 connect(m_list_handler, SIGNAL(SigItemToggleBrowseable()),
01933 SLOT(DoToggleBrowseable()));
01934 connect(m_list_handler, SIGNAL(SigItemChangeParental(int)),
01935 SLOT(OnParentalChange(int)));
01936 connect(m_list_handler, SIGNAL(SigDoFilter()),
01937 SLOT(DoFilter()));
01938 connect(m_list_handler, SIGNAL(SigDoMenu()),
01939 SLOT(DoVideoMenu()));
01940
01941 video_list->setCurrentVideoFilter(VideoFilterSettings(true,
01942 "VideoManager"));
01943
01944
01945
01946
01947 struct context_check
01948 {
01949 context_check(XMLParse *ltheme, const QString &name,
01950 int default_context)
01951 {
01952 LayerSet *s = ltheme->GetSet(name);
01953 if (s && s->GetContext() == edcAlwaysShown)
01954 {
01955 s->SetContext(default_context);
01956 }
01957 }
01958 };
01959
01960 context_check(theme, "moviesel", edcSearchListContext);
01961 context_check(theme, "enterimdb", edcManualUIDSearchContext);
01962 context_check(theme, "entersearchtitle",
01963 edcManualTitleSearchContext);
01964 context_check(theme, "inetwait", edcWaitContext);
01965
01966 RefreshVideoList(false);
01967
01968 m_has_manual_title_search = ManualSearchHandler::Exists(m_theme);
01969 connect(&m_url_dl_timer,
01970 SIGNAL(SigTimeout(const QString &, Metadata *)),
01971 SLOT(OnPosterDownloadTimeout(const QString &, Metadata *)));
01972 connect(&m_url_operator,
01973 SIGNAL(SigFinished(QNetworkOperation *, Metadata *)),
01974 SLOT(OnPosterCopyFinished(QNetworkOperation *,
01975 Metadata *)));
01976 }
01977
01978 ~VideoManagerImp()
01979 {
01980 m_current_item_proxy.disconnect();
01981 }
01982
01983 bool DispatchEvent(ContainerEvent &event_)
01984 {
01985 bool ret = m_event_dispatch.DispatchEvent(event_);
01986 if (!ret)
01987 {
01988
01989 }
01990
01991 return ret;
01992 }
01993
01994 void customEvent(QCustomEvent *e)
01995 {
01996 if (static_cast<int>(e->type()) ==
01997 ContainerDoneEvent::etContainerDone)
01998 {
01999 m_event_dispatch.ProcessDone();
02000 }
02001 }
02002
02003 private:
02004 void CancelPopup()
02005 {
02006 if (m_popup)
02007 {
02008 m_popup->deleteLater();
02009 m_popup = NULL;
02010 }
02011 }
02012
02013 void RefreshVideoList(bool resort_only);
02014
02015 static bool GetLocalVideoPoster(const QString &video_uid,
02016 const QString &filename,
02017 const QStringList &in_dirs,
02018 QString &poster)
02019 {
02020 QStringList search_dirs(in_dirs);
02021
02022 QFileInfo qfi(filename);
02023 search_dirs += qfi.dirPath(true);
02024
02025 const QString base_name = qfi.baseName(true);
02026 QStringList image_types = QImage::inputFormatList();
02027
02028 typedef std::set<QString> image_type_list;
02029 image_type_list image_exts;
02030
02031 for (QStringList::const_iterator it = image_types.begin();
02032 it != image_types.end(); ++it)
02033 {
02034 image_exts.insert((*it).lower());
02035 }
02036
02037 if (image_exts.find("jpeg") != image_exts.end())
02038 {
02039 image_exts.insert("jpg");
02040 }
02041
02042 const QString fntm("%1/%2.%3");
02043
02044 for (QStringList::const_iterator dir = search_dirs.begin();
02045 dir != search_dirs.end(); ++dir)
02046 {
02047 if (!(*dir).length()) continue;
02048
02049 for (image_type_list::const_iterator ext = image_exts.begin();
02050 ext != image_exts.end(); ++ext)
02051 {
02052 QStringList sfn;
02053 sfn += fntm.arg(*dir).arg(base_name).arg(*ext);
02054 sfn += fntm.arg(*dir).arg(video_uid).arg(*ext);
02055
02056 for (QStringList::const_iterator i = sfn.begin();
02057 i != sfn.end(); ++i)
02058 {
02059 if (QFile::exists(*i))
02060 {
02061 poster = *i;
02062 return true;
02063 }
02064 }
02065 }
02066 }
02067
02068 return false;
02069 }
02070
02071 void ResetItem(Metadata *item)
02072 {
02073 if (item)
02074 {
02075 item->Reset();
02076 item->updateDatabase();
02077
02078 RefreshVideoList(false);
02079 }
02080 }
02081
02082 void StartWaitBackground(const QString &text)
02083 {
02084 if (!m_wait_background)
02085 {
02086 m_wait_background =
02087 new WaitBackgroundHandler(this, m_vm, *m_theme);
02088 m_event_dispatch.push(m_wait_background);
02089 }
02090
02091 m_wait_background->EnterMessage(text);
02092 }
02093
02094 void StopWaitBackground()
02095 {
02096 if (m_wait_background)
02097 {
02098 if (!m_wait_background->LeaveMessage())
02099 {
02100 m_wait_background->Close();
02101 m_wait_background = 0;
02102 }
02103 }
02104 else
02105 {
02106 VERBOSE(VB_IMPORTANT, "Error: StopWaitBackground called with "
02107 "no active message.");
02108 }
02109 }
02110
02111 void AutomaticParentalAdjustment(Metadata *item);
02112
02113
02114
02115
02116 private:
02117
02118
02119
02120
02121
02122
02123
02124 void StartVideoPosterSet(Metadata *item);
02125
02126
02127
02128
02129 void StartVideoSearchByUID(const QString &video_uid, Metadata *item);
02130
02131
02132
02133
02134 void StartVideoSearchByTitle(const QString &video_uid,
02135 const QString &title, Metadata *item);
02136
02137
02138 private slots:
02139
02140 void OnPosterURL(const QString &uri, Metadata *item);
02141 void OnPosterCopyFinished(QNetworkOperation *op, Metadata *item);
02142 void OnPosterDownloadTimeout(const QString &url, Metadata *item);
02143
02144
02145 void OnVideoSearchByTitleDone(bool normal_exit,
02146 const SearchListHandler::item_list &results, Metadata *item);
02147
02148
02149 private slots:
02150
02151 void OnVideoPosterSetDone(Metadata *item);
02152
02153
02154 void OnVideoSearchByUIDDone(bool normal_exit,
02155 const QStringList &output,
02156 Metadata *item, const QString &video_uid);
02157
02158
02159 void OnVideoSearchByTitleDoneNoBackground(bool normal_exit,
02160 const SearchListHandler::item_list &results, Metadata *item);
02161
02162
02163
02164 private slots:
02165
02166 void DoEditMetadata();
02167 void DoRemoveVideo();
02168 void DoFilter();
02169 void DoManualVideoUID();
02170 void DoManualVideoTitle();
02171 void DoVideoSearchCurrentItem()
02172 {
02173 CancelPopup();
02174
02175 Metadata *item = m_list_handler->GetCurrentItem();
02176 if (item)
02177 {
02178 StartVideoSearchByTitle(item->InetRef(), item->Title(), item);
02179 }
02180 }
02181
02182 void DoVideoMenu()
02183 {
02184 m_popup = new MythPopupBox(gContext->GetMainWindow(),
02185 "video popup");
02186
02187 m_popup->addLabel(tr("Select action:"));
02188 m_popup->addLabel("");
02189
02190 QButton *editButton = NULL;
02191 if (m_list_handler->GetCurrentItem())
02192 {
02193 editButton = m_popup->addButton(tr("Edit Metadata"), this,
02194 SLOT(DoEditMetadata()));
02195 m_popup->addButton(tr("Search"), this,
02196 SLOT(DoVideoSearchCurrentItem()));
02197 m_popup->addButton(tr("Manually Enter Video #"), this,
02198 SLOT(DoManualVideoUID()));
02199 if (m_has_manual_title_search)
02200 {
02201 m_popup->addButton(tr("Manually Enter Video Title"), this,
02202 SLOT(DoManualVideoTitle()));
02203 }
02204 m_popup->addButton(tr("Reset Metadata"), this,
02205 SLOT(DoResetMetadata()));
02206 m_popup->addButton(tr("Toggle Browseable"), this,
02207 SLOT(DoToggleBrowseable()));
02208 m_popup->addButton(tr("Remove Video"), this,
02209 SLOT(DoRemoveVideo()));
02210 }
02211
02212 QButton *filterButton =
02213 m_popup->addButton(tr("Filter Display"), this,
02214 SLOT(DoFilter()));
02215 m_popup->addButton(tr("Cancel"), this, SLOT(OnVideoMenuDone()));
02216
02217 m_popup->ShowPopup(this, SLOT(OnVideoMenuDone()));
02218 m_popup->setActiveWindow();
02219
02220 if (editButton)
02221 editButton->setFocus();
02222 else
02223 filterButton->setFocus();
02224 }
02225
02226 void DoToggleBrowseable()
02227 {
02228 CancelPopup();
02229
02230 Metadata *item = m_list_handler->GetCurrentItem();
02231 if (item)
02232 {
02233 item->setBrowse(!item->Browse());
02234 item->updateDatabase();
02235
02236 RefreshVideoList(false);
02237 OnSelectedItemChange();
02238 }
02239 }
02240
02241 void OnParentalChange(int amount);
02242
02243
02244 void OnListSelectionChange()
02245 {
02246 m_info_handler->Update();
02247 }
02248
02249
02250 void OnSelectedItemChange()
02251 {
02252 m_info_handler->Update();
02253 m_list_handler->Update();
02254 }
02255
02256 void DoResetMetadata();
02257
02258 void OnVideoMenuDone();
02259
02260 void OnVideoSearchListCancel()
02261 {
02262
02263
02264 Metadata *item = m_list_handler->GetCurrentItem();
02265
02266 if (item && isDefaultCoverFile(item->CoverFile()))
02267 {
02268 QStringList search_dirs;
02269 search_dirs += m_art_dir;
02270 QString cover_file;
02271
02272 if (GetLocalVideoPoster(item->InetRef(), item->Filename(),
02273 search_dirs, cover_file))
02274 {
02275 item->setCoverFile(cover_file);
02276 item->updateDatabase();
02277 RefreshVideoList(true);
02278 }
02279 }
02280 }
02281
02282 void OnVideoSearchListReset()
02283 {
02284 DoResetMetadata();
02285 }
02286
02287 void OnVideoSearchListManual()
02288 {
02289 DoManualVideoUID();
02290 }
02291
02292 void OnVideoSearchListManualTitle()
02293 {
02294 DoManualVideoTitle();
02295 }
02296
02297 void OnVideoSearchListSelection(const QString &video_uid,
02298 const QString &video_title)
02299 {
02300 (void) video_title;
02301 Metadata *item = m_list_handler->GetCurrentItem();
02302 if (item && video_uid.length())
02303 {
02304 StartVideoSearchByUID(video_uid, item);
02305 }
02306 }
02307
02308 void OnManualVideoUID(const QString &video_uid);
02309 void OnManualVideoTitle(const QString &title);
02310
02311 private:
02312 ContainerDispatch<ContainerHandler, VideoManager> m_event_dispatch;
02313 VideoManager *m_vm;
02314 XMLParse *m_theme;
02315 QRect m_area;
02316 VideoList *m_video_list;
02317 InfoHandler *m_info_handler;
02318 ListHandler *m_list_handler;
02319 MythPopupBox *m_popup;
02320 WaitBackgroundHandler *m_wait_background;
02321 CurrentItemGet m_current_item_proxy;
02322 QString m_art_dir;
02323 bool m_has_manual_title_search;
02324 URLOperationProxy m_url_operator;
02325 TimeoutSignalProxy m_url_dl_timer;
02326 parental_level_map m_rating_to_pl;
02327 };
02328
02329 void VideoManagerImp::RefreshVideoList(bool resort_only)
02330 {
02331 static bool updateML = false;
02332 if (updateML == true)
02333 return;
02334 updateML = true;
02335
02336 unsigned int selected_id = 0;
02337 const Metadata *item = m_list_handler->GetCurrentItem();
02338 if (item)
02339 selected_id = item->ID();
02340
02341 if (resort_only)
02342 {
02343 m_video_list->resortList(true);
02344 }
02345 else
02346 {
02347 m_video_list->refreshList(false,
02348 ParentalLevel(ParentalLevel::plNone), true);
02349 }
02350
02351 m_list_handler->OnListChanged();
02352
02353
02354
02355 if (selected_id)
02356 {
02357 MetadataListManager::MetadataPtr sel_item =
02358 m_video_list->getListCache().byID(selected_id);
02359 if (sel_item)
02360 {
02361 m_list_handler->SetSelectedItem(sel_item->getFlatIndex());
02362 }
02363 }
02364
02365 updateML = false;
02366 }
02367
02368 void VideoManagerImp::AutomaticParentalAdjustment(Metadata *item)
02369 {
02370 if (item && m_rating_to_pl.size())
02371 {
02372 QString rating = item->Rating();
02373 for (parental_level_map::const_iterator p = m_rating_to_pl.begin();
02374 rating.length() && p != m_rating_to_pl.end(); ++p)
02375 {
02376 if (rating.find(p->first) != -1)
02377 {
02378 item->setShowLevel(p->second);
02379 break;
02380 }
02381 }
02382 }
02383 }
02384
02385
02386
02387
02388 void VideoManagerImp::StartVideoPosterSet(Metadata *item)
02389 {
02390 StartWaitBackground(QObject::tr("Fetching poster for %1 (%2)")
02391 .arg(item->InetRef())
02392 .arg(item->Title()));
02393 QStringList search_dirs;
02394 search_dirs += m_art_dir;
02395
02396 QString cover_file;
02397
02398 if (GetLocalVideoPoster(item->InetRef(), item->Filename(), search_dirs,
02399 cover_file))
02400 {
02401 item->setCoverFile(cover_file);
02402 OnVideoPosterSetDone(item);
02403 return;
02404 }
02405
02406
02407 VideoPosterSearch *vps = new VideoPosterSearch(this);
02408 connect(vps, SIGNAL(SigPosterURL(const QString &, Metadata *)),
02409 SLOT(OnPosterURL(const QString &, Metadata *)));
02410 vps->Run(item->InetRef(), item);
02411 }
02412
02413 void VideoManagerImp::OnPosterURL(const QString &uri, Metadata *item)
02414 {
02415 if (item)
02416 {
02417 if (uri.length())
02418 {
02419 QString fileprefix = m_art_dir;
02420
02421 QDir dir;
02422
02423
02424
02425 if (fileprefix.length() == 0)
02426 {
02427 fileprefix = MythContext::GetConfDir();
02428
02429 dir.setPath(fileprefix);
02430 if (!dir.exists())
02431 dir.mkdir(fileprefix);
02432
02433 fileprefix += "/MythVideo";
02434 }
02435
02436 dir.setPath(fileprefix);
02437 if (!dir.exists())
02438 dir.mkdir(fileprefix);
02439
02440 QUrl url(uri);
02441
02442 QString ext = QFileInfo(url.fileName()).extension(false);
02443 QString dest_file = QString("%1/%2.%3").arg(fileprefix)
02444 .arg(item->InetRef()).arg(ext);
02445 VERBOSE(VB_IMPORTANT, QString("Copying '%1' -> '%2'...")
02446 .arg(uri).arg(dest_file));
02447
02448 item->setCoverFile(dest_file);
02449
02450 m_url_operator.copy(uri, QString("file:%1").arg(dest_file),
02451 item);
02452 VERBOSE(VB_IMPORTANT,
02453 QString("dest_file = %1").arg(dest_file));
02454
02455 const int nTimeout =
02456 gContext->GetNumSetting("PosterDownloadTimeout", 30)
02457 * 1000;
02458 m_url_dl_timer.start(nTimeout, item, url);
02459 }
02460 else
02461 {
02462 item->setCoverFile("");
02463 OnVideoPosterSetDone(item);
02464 }
02465 }
02466 else
02467 OnVideoPosterSetDone(item);
02468 }
02469
02470 void VideoManagerImp::OnPosterCopyFinished(QNetworkOperation *op,
02471 Metadata *item)
02472 {
02473 m_url_dl_timer.stop();
02474 QString state, operation;
02475 switch(op->operation())
02476 {
02477 case QNetworkProtocol::OpMkDir:
02478 operation = "MkDir";
02479 break;
02480 case QNetworkProtocol::OpRemove:
02481 operation = "Remove";
02482 break;
02483 case QNetworkProtocol::OpRename:
02484 operation = "Rename";
02485 break;
02486 case QNetworkProtocol::OpGet:
02487 operation = "Get";
02488 break;
02489 case QNetworkProtocol::OpPut:
02490 operation = "Put";
02491 break;
02492 default:
02493 operation = "Uknown";
02494 break;
02495 }
02496
02497 switch(op->state())
02498 {
02499 case QNetworkProtocol::StWaiting:
02500 state = "The operation is in the QNetworkProtocol's queue "
02501 "waiting to be prcessed.";
02502 break;
02503 case QNetworkProtocol::StInProgress:
02504 state = "The operation is being processed.";
02505 break;
02506 case QNetworkProtocol::StDone:
02507 state = "The operation has been processed succesfully.";
02508 break;
02509 case QNetworkProtocol::StFailed:
02510 state = "The operation has been processed but an error "
02511 "occurred.";
02512 if (item)
02513 item->setCoverFile("");
02514 break;
02515 case QNetworkProtocol::StStopped:
02516 state = "The operation has been processed but has been stopped "
02517 "before it finished, and is waiting to be processed.";
02518 break;
02519 default:
02520 state = "Unknown";
02521 break;
02522 }
02523
02524 VERBOSE(VB_IMPORTANT, QString("%1: %2: %3").arg(operation).arg(state)
02525 .arg(op->protocolDetail()));
02526
02527 OnVideoPosterSetDone(item);
02528 }
02529
02530 void VideoManagerImp::OnPosterDownloadTimeout(const QString &url,
02531 Metadata *item)
02532 {
02533 VERBOSE(VB_IMPORTANT, QString("Copying of '%1' timed out").arg(url));
02534
02535 if (item)
02536 item->setCoverFile("");
02537
02538 m_url_operator.stop();
02539
02540 MythPopupBox::showOkPopup(gContext->GetMainWindow(),
02541 QObject::tr("Could not retrieve poster"),
02542 QObject::tr("A poster exists for this item but could not be "
02543 "retrieved within the timeout period.\n"));
02544 }
02545
02546
02547 void VideoManagerImp::OnVideoPosterSetDone(Metadata *item)
02548 {
02549
02550 StopWaitBackground();
02551
02552 item->updateDatabase();
02553 RefreshVideoList(true);
02554 OnSelectedItemChange();
02555 }
02556
02557 void VideoManagerImp::StartVideoSearchByUID(const QString &video_uid,
02558 Metadata *item)
02559 {
02560 StartWaitBackground(video_uid);
02561 VideoUIDSearch *vns = new VideoUIDSearch(this);
02562 connect(vns, SIGNAL(SigSearchResults(bool, const QStringList &,
02563 Metadata *, const QString &)),
02564 SLOT(OnVideoSearchByUIDDone(bool, const QStringList &,
02565 Metadata *, const QString &)));
02566 vns->Run(video_uid, item);
02567 }
02568
02569 void VideoManagerImp::OnVideoSearchByUIDDone(bool normal_exit,
02570 const QStringList &output,
02571 Metadata *item,
02572 const QString &video_uid)
02573 {
02574 StopWaitBackground();
02575
02576 std::map<QString, QString> data;
02577
02578 if (normal_exit && output.size())
02579 {
02580 for (QStringList::const_iterator p = output.begin();
02581 p != output.end(); ++p)
02582 {
02583 data[(*p).section(':', 0, 0)] = (*p).section(':', 1);
02584 }
02585
02586 item->setTitle(data["Title"]);
02587 item->setYear(data["Year"].toInt());
02588 item->setDirector(data["Director"]);
02589 item->setPlot(data["Plot"]);
02590 item->setUserRating(data["UserRating"].toFloat());
02591 item->setRating(data["MovieRating"]);
02592 item->setLength(data["Runtime"].toInt());
02593
02594 AutomaticParentalAdjustment(item);
02595
02596
02597 Metadata::cast_list cast;
02598 QStringList cl = QStringList::split(",", data["Cast"]);
02599
02600 for (QStringList::const_iterator p = cl.begin();
02601 p != cl.end(); ++p)
02602 {
02603 QString cn = (*p).stripWhiteSpace();
02604 if (cn.length())
02605 {
02606 cast.push_back(Metadata::cast_list::
02607 value_type(-1, cn));
02608 }
02609 }
02610
02611 item->setCast(cast);
02612
02613
02614 Metadata::genre_list video_genres;
02615 QStringList genres = QStringList::split(",", data["Genres"]);
02616
02617 for (QStringList::iterator p = genres.begin(); p != genres.end();
02618 ++p)
02619 {
02620 QString genre_name = (*p).stripWhiteSpace();
02621 if (genre_name.length())
02622 {
02623 video_genres.push_back(
02624 Metadata::genre_list::value_type(-1, genre_name));
02625 }
02626 }
02627
02628 item->setGenres(video_genres);
02629
02630
02631 Metadata::country_list video_countries;
02632 QStringList countries = QStringList::split(",", data["Countries"]);
02633 for (QStringList::iterator p = countries.begin();
02634 p != countries.end(); ++p)
02635 {
02636 QString country_name = (*p).stripWhiteSpace();
02637 if (country_name.length())
02638 {
02639 video_countries.push_back(
02640 Metadata::country_list::value_type(-1,
02641 country_name));
02642 }
02643 }
02644
02645 item->setCountries(video_countries);
02646
02647 item->setInetRef(video_uid);
02648 StartVideoPosterSet(item);
02649 }
02650 else
02651 {
02652 ResetItem(item);
02653 item->updateDatabase();
02654 RefreshVideoList(true);
02655 OnSelectedItemChange();
02656 }
02657 }
02658
02659 void VideoManagerImp::StartVideoSearchByTitle(const QString &video_uid,
02660 const QString &title,
02661 Metadata *item)
02662 {
02663 if (video_uid == VIDEO_INETREF_DEFAULT)
02664 {
02665 StartWaitBackground(title);
02666
02667 VideoTitleSearch *vts = new VideoTitleSearch(this);
02668 connect(vts,
02669 SIGNAL(SigSearchResults(bool,
02670 const SearchListHandler::item_list &, Metadata *)),
02671 SLOT(OnVideoSearchByTitleDone(bool,
02672 const SearchListHandler::item_list &, Metadata *)));
02673 vts->Run(title, item);
02674 }
02675 else
02676 {
02677 typedef SearchListHandler::item_list item_list;
02678 item_list videos;
02679 videos.push_back(item_list::value_type(video_uid, title));
02680 OnVideoSearchByTitleDoneNoBackground(true, videos, item);
02681 }
02682 }
02683
02684 void VideoManagerImp::OnVideoSearchByTitleDone(bool normal_exit,
02685 const SearchListHandler::item_list &results, Metadata *item)
02686 {
02687 StopWaitBackground();
02688 OnVideoSearchByTitleDoneNoBackground(normal_exit, results, item);
02689 }
02690
02691
02692 void VideoManagerImp::OnVideoSearchByTitleDoneNoBackground(bool normal_exit,
02693 const SearchListHandler::item_list &results, Metadata *item)
02694 {
02695 (void) normal_exit;
02696 VERBOSE(VB_IMPORTANT,
02697 QString("GetVideoList returned %1 possible matches")
02698 .arg(results.size()));
02699
02700 if (results.size() == 1)
02701 {
02702
02703 if (results.front().first.length() == 0)
02704 {
02705 ResetItem(item);
02706 OnSelectedItemChange();
02707 return;
02708 }
02709 StartVideoSearchByUID(results.front().first, item);
02710 }
02711 else
02712 {
02713 SearchListHandler *slh =
02714 new SearchListHandler(this, m_vm, *m_theme, results,
02715 m_has_manual_title_search);
02716 connect(slh, SIGNAL(SigItemSelected(const QString &,
02717 const QString &)),
02718 SLOT(OnVideoSearchListSelection(const QString &,
02719 const QString &)));
02720 connect(slh, SIGNAL(SigCancel()), SLOT(OnVideoSearchListCancel()));
02721 connect(slh, SIGNAL(SigReset()), SLOT(OnVideoSearchListReset()));
02722 connect(slh, SIGNAL(SigManual()), SLOT(OnVideoSearchListManual()));
02723 connect(slh, SIGNAL(SigManualTitle()),
02724 SLOT(OnVideoSearchListManualTitle()));
02725
02726 m_event_dispatch.push(slh);
02727 }
02728 }
02729
02730 void VideoManagerImp::OnParentalChange(int amount)
02731 {
02732 Metadata *item = m_list_handler->GetCurrentItem();
02733 if (item)
02734 {
02735 ParentalLevel curshowlevel = item->ShowLevel();
02736
02737 curshowlevel += amount;
02738
02739 if (curshowlevel.GetLevel() != item->ShowLevel())
02740 {
02741 item->setShowLevel(curshowlevel.GetLevel());
02742 item->updateDatabase();
02743 RefreshVideoList(true);
02744 OnSelectedItemChange();
02745 }
02746 }
02747 }
02748
02749 void VideoManagerImp::DoManualVideoUID()
02750 {
02751 CancelPopup();
02752 ManualSearchUIDHandler *muidh =
02753 new ManualSearchUIDHandler(this, m_vm, *m_theme);
02754 connect(muidh, SIGNAL(SigTextChanged(const QString &)),
02755 SLOT(OnManualVideoUID(const QString &)));
02756
02757 m_event_dispatch.push(muidh);
02758 }
02759
02760 void VideoManagerImp::OnManualVideoUID(const QString &video_uid)
02761 {
02762 if (video_uid.length())
02763 {
02764 StartVideoSearchByUID(video_uid, m_list_handler->GetCurrentItem());
02765 }
02766 }
02767
02768 void VideoManagerImp::DoManualVideoTitle()
02769 {
02770 CancelPopup();
02771 ManualSearchHandler *msh =
02772 new ManualSearchHandler(this, m_vm, *m_theme);
02773 connect(msh, SIGNAL(SigTextChanged(const QString &)),
02774 SLOT(OnManualVideoTitle(const QString &)));
02775
02776 m_event_dispatch.push(msh);
02777 }
02778
02779 void VideoManagerImp::OnManualVideoTitle(const QString &title)
02780 {
02781 Metadata *item = m_list_handler->GetCurrentItem();
02782 if (title.length() && item)
02783 {
02784 StartVideoSearchByTitle(VIDEO_INETREF_DEFAULT, title, item);
02785 }
02786 }
02787
02788 void VideoManagerImp::DoEditMetadata()
02789 {
02790 CancelPopup();
02791
02792 Metadata *item = m_list_handler->GetCurrentItem();
02793 if (!item) return;
02794
02795 EditMetadataDialog *md_editor = new EditMetadataDialog(
02796 item, m_video_list->getListCache(), gContext->GetMainWindow(),
02797 "edit_metadata", "video-", "edit metadata dialog");
02798
02799 md_editor->exec();
02800 delete md_editor;
02801
02802 RefreshVideoList(false);
02803
02804 OnSelectedItemChange();
02805 }
02806
02807 void VideoManagerImp::DoRemoveVideo()
02808 {
02809 CancelPopup();
02810
02811 Metadata *item = m_list_handler->GetCurrentItem();
02812
02813 if (item)
02814 {
02815 bool okcancel;
02816 MythPopupBox *confirmationDialog =
02817 new MythPopupBox(gContext->GetMainWindow());
02818 okcancel = confirmationDialog->showOkCancelPopup(
02819 gContext->GetMainWindow(), "", tr("Delete this file?"),
02820 false);
02821
02822 if (okcancel)
02823 {
02824 if (m_video_list->Delete(item->ID()))
02825 RefreshVideoList(false);
02826 else
02827 confirmationDialog->showOkPopup(gContext->GetMainWindow(),
02828 "", tr("delete failed"));
02829 }
02830
02831 confirmationDialog->deleteLater();
02832 }
02833 }
02834
02835 void VideoManagerImp::OnVideoMenuDone()
02836 {
02837 if (m_popup)
02838 {
02839 CancelPopup();
02840 }
02841 }
02842
02843 void VideoManagerImp::DoResetMetadata()
02844 {
02845 CancelPopup();
02846
02847 Metadata *item = m_list_handler->GetCurrentItem();
02848 if (item)
02849 {
02850 ResetItem(item);
02851
02852 QString cover_file;
02853 QStringList search_dirs;
02854 search_dirs += m_art_dir;
02855 if (GetLocalVideoPoster(item->InetRef(), item->Filename(),
02856 search_dirs, cover_file))
02857 {
02858 item->setCoverFile(cover_file);
02859 item->updateDatabase();
02860 RefreshVideoList(true);
02861 }
02862
02863 OnSelectedItemChange();
02864 }
02865 }
02866
02867 void VideoManagerImp::DoFilter()
02868 {
02869 CancelPopup();
02870
02871 m_video_list->getFilterChangedState();
02872 BasicFilterSettingsProxy<VideoList> sp(*m_video_list);
02873 VideoFilterDialog *vfd =
02874 new VideoFilterDialog(&sp, gContext->GetMainWindow(),
02875 "filter", "video-", *m_video_list,
02876 "Video Filter Dialog");
02877 vfd->exec();
02878 delete vfd;
02879
02880 unsigned int filter_state = m_video_list->getFilterChangedState();
02881 if (filter_state & VideoFilterSettings::FILTER_MASK)
02882 {
02883 RefreshVideoList(false);
02884 }
02885 else if (filter_state & VideoFilterSettings::kSortOrderChanged)
02886 {
02887 RefreshVideoList(true);
02888 }
02889 }
02890 };
02891
02892 VideoManager::VideoManager(MythMainWindow *lparent, VideoList *video_list) :
02893 MythThemedDialog(lparent, "manager", "video-", "video manager")
02894 {
02895 m_imp.reset(new mythvideo_videomanager::VideoManagerImp(this, getTheme(),
02896 QRect(0, 0, size().width(), size().height()),
02897 video_list));
02898 buildFocusList();
02899 assignFirstFocus();
02900 }
02901
02902 VideoManager::~VideoManager()
02903 {
02904 }
02905
02906 void VideoManager::keyPressEvent(QKeyEvent *event_)
02907 {
02908 bool handled = false;
02909
02910 QStringList actions;
02911 gContext->GetMainWindow()->TranslateKeyPress("Video", event_, actions);
02912
02913 for (QStringList::iterator p = actions.begin();
02914 p != actions.end() && !handled; ++p)
02915 {
02916 mythvideo_videomanager::CEKeyPress kp(*p);
02917 m_imp->DispatchEvent(kp);
02918 handled = kp.GetHandled();
02919 }
02920
02921 if (!handled)
02922 MythThemedDialog::keyPressEvent(event_);
02923 }
02924
02925 void VideoManager::ExitWin()
02926 {
02927 emit accept();
02928 }
02929
02930 #include "videomanager.moc"