MythTV  master
videoselector.cpp
Go to the documentation of this file.
1 // c
2 #include <chrono>
3 #include <cstdlib>
4 #include <unistd.h>
5 
6 // qt
7 #include <QDir>
8 #include <QTimer>
9 
10 // mythtv
11 #include <libmyth/mythcontext.h>
12 #include <libmythbase/mythdb.h>
13 #include <libmythbase/remotefile.h>
14 #include <libmythbase/stringutil.h>
18 #include <libmythui/mythuibutton.h>
20 #include <libmythui/mythuiimage.h>
21 #include <libmythui/mythuitext.h>
22 
23 // mytharchive
24 #include "archiveutil.h"
25 #include "videoselector.h"
26 
27 VideoSelector::VideoSelector(MythScreenStack *parent, QList<ArchiveItem *> *archiveList)
28  :MythScreenType(parent, "VideoSelector"),
29  m_archiveList(archiveList)
30 {
34 }
35 
37 {
38  delete m_videoList;
39  while (!m_selectedList.isEmpty())
40  delete m_selectedList.takeFirst();
41  m_selectedList.clear();
43 }
44 
46 {
47  // Load the theme for this screen
48  bool foundtheme = LoadWindowFromXML("mytharchive-ui.xml", "video_selector", this);
49  if (!foundtheme)
50  return false;
51 
52  bool err = false;
53  UIUtilE::Assign(this, m_okButton, "ok_button", &err);
54  UIUtilE::Assign(this, m_cancelButton, "cancel_button", &err);
55  UIUtilE::Assign(this, m_categorySelector, "category_selector", &err);
56  UIUtilE::Assign(this, m_videoButtonList, "videolist", &err);
57  UIUtilE::Assign(this, m_titleText, "videotitle", &err);
58  UIUtilE::Assign(this, m_plotText, "videoplot", &err);
59  UIUtilE::Assign(this, m_filesizeText, "filesize", &err);
60  UIUtilE::Assign(this, m_coverImage, "cover_image", &err);
61  UIUtilE::Assign(this, m_warningText, "warning_text", &err);
62  UIUtilE::Assign(this, m_plText, "parentallevel_text", &err);
63 
64  if (err)
65  {
66  LOG(VB_GENERAL, LOG_ERR, "Cannot load screen 'video_selector'");
67  return false;
68  }
69 
72 
75 
76  getVideoList();
81 
83 
85 
87 
90 
91  return true;
92 }
93 
94 bool VideoSelector::keyPressEvent(QKeyEvent *event)
95 {
96  if (GetFocusWidget()->keyPressEvent(event))
97  return true;
98 
99  QStringList actions;
100  bool handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
101 
102  for (int i = 0; i < actions.size() && !handled; i++)
103  {
104  QString action = actions[i];
105  handled = true;
106 
107  if (action == "MENU")
108  {
109  ShowMenu();
110  }
111  else if (action == "1")
112  {
114  }
115  else if (action == "2")
116  {
118  }
119  else if (action == "3")
120  {
122  }
123  else if (action == "4")
124  {
126  }
127  else
128  handled = false;
129  }
130 
131  if (!handled && MythScreenType::keyPressEvent(event))
132  handled = true;
133 
134  return handled;
135 }
136 
138 {
139  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
140 
141  auto *menuPopup = new MythDialogBox(tr("Menu"), popupStack, "actionmenu");
142 
143  if (menuPopup->Create())
144  popupStack->AddScreen(menuPopup);
145 
146  menuPopup->SetReturnEvent(this, "action");
147 
148  menuPopup->AddButton(tr("Clear All"), &VideoSelector::clearAll);
149  menuPopup->AddButton(tr("Select All"), &VideoSelector::selectAll);
150 }
151 
153 {
154  while (!m_selectedList.isEmpty())
155  m_selectedList.takeFirst();
156  m_selectedList.clear();
157 
158  for (auto *v : *m_videoList)
159  m_selectedList.append(v);
160 
161  updateVideoList();
162 }
163 
165 {
166  while (!m_selectedList.isEmpty())
167  m_selectedList.takeFirst();
168  m_selectedList.clear();
169 
170  updateVideoList();
171 }
172 
174 {
176  {
177  int index = m_selectedList.indexOf(item->GetData().value<VideoInfo *>());
178  if (index != -1)
179  m_selectedList.takeAt(index);
181  }
182  else
183  {
184  int index = m_selectedList.indexOf(item->GetData().value<VideoInfo *>());
185  if (index == -1)
186  m_selectedList.append(item->GetData().value<VideoInfo *>());
187 
189  }
190 }
191 
193 {
194  auto *v = item->GetData().value<VideoInfo *>();
195 
196  if (!v)
197  return;
198 
199  if (m_titleText)
200  m_titleText->SetText(v->title);
201 
202  if (m_plotText)
203  m_plotText->SetText(v->plot);
204 
205  if (m_coverImage)
206  {
207  if (v->coverfile != "" && v->coverfile != "No Cover")
208  {
209  m_coverImage->SetFilename(v->coverfile);
210  m_coverImage->Load();
211  }
212  else
213  {
214  m_coverImage->SetFilename("blank.png");
215  m_coverImage->Load();
216  }
217  }
218 
219  if (m_filesizeText)
220  {
221  if (v->size == 0)
222  {
223  struct stat fileinfo {};
224 
225  bool bExists = RemoteFile::Exists(v->filename, &fileinfo);
226  if (bExists)
227  v->size = (uint64_t)fileinfo.st_size;
228 
229  if (!bExists)
230  {
231  LOG(VB_GENERAL, LOG_ERR,
232  QString("VideoSelector: Cannot find file: %1")
233  .arg(v->filename));
234  }
235  }
236 
238  }
239 }
240 
242 {
243  // loop though selected videos and add them to the list
244  // remove any items that have been removed from the list
245  QList<ArchiveItem *> tempAList;
246  for (auto *a : std::as_const(*m_archiveList))
247  {
248  bool found = false;
249 
250  for (const auto *v : std::as_const(m_selectedList))
251  {
252  if (a->type != "Video" || a->filename == v->filename)
253  {
254  found = true;
255  break;
256  }
257  }
258 
259  if (!found)
260  tempAList.append(a);
261  }
262 
263  for (auto *x : std::as_const(tempAList))
264  m_archiveList->removeAll(x);
265 
266  // remove any items that are already in the list
267  QList<VideoInfo *> tempSList;
268  for (auto *v : std::as_const(m_selectedList))
269  {
270  for (const auto *a : std::as_const(*m_archiveList))
271  {
272  if (a->filename == v->filename)
273  {
274  tempSList.append(v);
275  break;
276  }
277  }
278  }
279 
280  for (auto *x : std::as_const(tempSList))
281  m_selectedList.removeAll(x);
282 
283  // add all that are left
284  for (const auto *v : std::as_const(m_selectedList))
285  {
286  auto *a = new ArchiveItem;
287  a->type = "Video";
288  a->title = v->title;
289  a->subtitle = "";
290  a->description = v->plot;
291  a->startDate = "";
292  a->startTime = "";
293  a->size = v->size;
294  a->newsize = v->size;
295  a->filename = v->filename;
296  a->hasCutlist = false;
297  a->useCutlist = false;
298  a->duration = 0;
299  a->cutDuration = 0;
300  a->videoWidth = 0;
301  a->videoHeight = 0;
302  a->fileCodec = "";
303  a->videoCodec = "";
304  a->encoderProfile = nullptr;
305  a->editedDetails = false;
306  m_archiveList->append(a);
307  }
308 
309  emit haveResult(true);
310  Close();
311 }
312 
314 {
315  emit haveResult(false);
316  Close();
317 }
318 
320 {
321  if (!m_videoList)
322  return;
323 
325 
326  if (m_categorySelector)
327  {
328  for (auto *v : *m_videoList)
329  {
330  if (v->category == m_categorySelector->GetValue() ||
331  m_categorySelector->GetValue() == tr("All Videos"))
332  {
333  if (v->parentalLevel <= m_currentParentalLevel)
334  {
335  auto* item = new MythUIButtonListItem(m_videoButtonList,
336  v->title);
337  item->setCheckable(true);
338  if (m_selectedList.indexOf(v) != -1)
339  {
340  item->setChecked(MythUIButtonListItem::FullChecked);
341  }
342  else
343  {
344  item->setChecked(MythUIButtonListItem::NotChecked);
345  }
346 
347  item->SetData(QVariant::fromValue(v));
348  }
349  }
350  }
351  }
352 
353  if (m_videoButtonList->GetCount() > 0)
354  {
357  m_warningText->Hide();
358  }
359  else
360  {
361  m_warningText->Show();
362  m_titleText->Reset();
363  m_plotText->Reset();
364  m_coverImage->SetFilename("blank.png");
365  m_coverImage->Load();
367  }
368 }
369 
370 std::vector<VideoInfo *> *VideoSelector::getVideoListFromDB(void)
371 {
372  // get a list of category's
373  using CategoryMap = QMap<int, QString>;
374  CategoryMap categoryMap;
375  MSqlQuery query(MSqlQuery::InitCon());
376  query.prepare("SELECT intid, category FROM videocategory");
377 
378  if (query.exec())
379  {
380  while (query.next())
381  {
382  int id = query.value(0).toInt();
383  QString category = query.value(1).toString();
384  categoryMap.insert(id, category);
385  }
386  }
387 
388  query.prepare("SELECT intid, title, plot, length, filename, coverfile, "
389  "category, showlevel, subtitle, season, episode, host "
390  "FROM videometadata ORDER BY title,season,episode");
391 
392  if (query.exec() && query.size())
393  {
394  auto *videoList = new std::vector<VideoInfo*>;
395  QString episode;
396  while (query.next())
397  {
398  // Exclude iso images as they aren't supported
399  QString filename = query.value(4).toString();
400  if (filename.endsWith(".iso") || filename.endsWith(".ISO"))
401  continue;
402 
403  auto *info = new VideoInfo;
404 
405  info->id = query.value(0).toInt();
406  if (query.value(9).toInt() > 0)
407  {
408  episode = query.value(10).toString();
409  if (episode.size() < 2)
410  episode.prepend("0");
411  info->title = QString("%1 %2x%3 - %4")
412  .arg(query.value(1).toString(),
413  query.value(9).toString(),
414  episode,
415  query.value(8).toString());
416  }
417  else
418  info->title = query.value(1).toString();
419 
420  info->plot = query.value(2).toString();
421  info->size = 0; //query.value(3).toInt();
422  QString host = query.value(11).toString();
423 
424  // try to find the file locally
425  if (host.isEmpty())
426  {
427  // must already be a local filename?
428  info->filename = filename;
429  }
430  else
431  {
432  // if the file is on this host then we should be able to find it locally
433  if (host == gCoreContext->GetHostName())
434  {
435  StorageGroup videoGroup("Videos", gCoreContext->GetHostName(), false);
436  info->filename = videoGroup.FindFile(filename);
437 
438  // sanity check the file exists
439  if (!QFile::exists(info->filename))
440  {
441  LOG(VB_GENERAL, LOG_ERR,
442  QString("VideoSelector: Failed to find local file '%1'").arg(info->filename));
443  info->filename.clear();
444  }
445  }
446 
447  if (info->filename.isEmpty())
448  {
449  // file must not be local or doesn't exist
450  info->filename = generate_file_url("Videos", host, filename);
451  }
452  }
453 
454  LOG(VB_FILE, LOG_INFO,
455  QString("VideoSelector: found file '%1'").arg(info->filename));
456 
457  info->coverfile = query.value(5).toString();
458  info->category = categoryMap[query.value(6).toInt()];
459  info->parentalLevel = query.value(7).toInt();
460  if (info->category.isEmpty())
461  info->category = "(None)";
462  videoList->push_back(info);
463  }
464 
465  return videoList;
466  }
467 
468  LOG(VB_GENERAL, LOG_ERR, "VideoSelector: Failed to get any videos");
469 
470  return nullptr;
471 }
472 
474 {
476  QStringList categories;
477 
478  if (m_videoList && !m_videoList->empty())
479  {
480  for (auto *v : *m_videoList)
481  {
482  if (categories.indexOf(v->category) == -1)
483  categories.append(v->category);
484  }
485  }
486  else
487  {
488  QTimer::singleShot(100ms, this, &VideoSelector::cancelPressed);
489  return;
490  }
491 
492  // sort and add categories to selector
493  categories.sort();
494 
495  if (m_categorySelector)
496  {
497  new MythUIButtonListItem(m_categorySelector, tr("All Videos"));
499 
500  for (int x = 0; x < categories.count(); x++)
501  {
502  new MythUIButtonListItem(m_categorySelector, categories[x]);
503  }
504  }
505 
506  setCategory(nullptr);
507 }
508 
510 {
511  updateVideoList();
512 }
513 
515 {
516  if (!m_videoList)
517  return;
518 
519  m_selectedList.clear();
520 
521  for (const auto *a : std::as_const(*m_archiveList))
522  {
523  for (auto *v : std::as_const(*m_videoList))
524  {
525  if (v->filename == a->filename)
526  {
527  if (m_selectedList.indexOf(v) == -1)
528  m_selectedList.append(v);
529  break;
530  }
531  }
532  }
533 }
534 
536 {
538 }
539 
541 {
542  if (passwordValid)
543  {
544  m_currentParentalLevel = newLevel;
545  updateVideoList();
546  m_plText->SetText(QString::number(newLevel));
547  }
548  else
549  ShowOkPopup(tr("You need to enter a valid password for this parental level"));
550 }
551 
MythUIButton::Clicked
void Clicked()
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
generate_file_url
QString generate_file_url(const QString &storage_group, const QString &host, const QString &path)
Definition: videoutils.h:65
VideoSelector::clearAll
void clearAll(void)
Definition: videoselector.cpp:164
VideoSelector::setCategory
void setCategory(MythUIButtonListItem *item)
Definition: videoselector.cpp:509
VideoSelector::m_categorySelector
MythUIButtonList * m_categorySelector
Definition: videoselector.h:88
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:214
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1591
mythuitext.h
MythUIText::Reset
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:82
ParentalLevel::plLowest
@ plLowest
Definition: parentalcontrols.h:12
VideoSelector::m_plotText
MythUIText * m_plotText
Definition: videoselector.h:91
mythdb.h
VideoSelector::haveResult
void haveResult(bool ok)
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:386
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:454
VideoSelector::m_warningText
MythUIText * m_warningText
Definition: videoselector.h:85
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:597
MythUIImage::Load
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
Definition: mythuiimage.cpp:966
ParentalLevel::plMedium
@ plMedium
Definition: parentalcontrols.h:12
ParentalLevel::plHigh
@ plHigh
Definition: parentalcontrols.h:13
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
ParentalLevel::plLow
@ plLow
Definition: parentalcontrols.h:12
VideoSelector::toggleSelected
void toggleSelected(MythUIButtonListItem *item)
Definition: videoselector.cpp:173
mythdialogbox.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
MythScreenStack
Definition: mythscreenstack.h:16
MythUIButtonListItem::FullChecked
@ FullChecked
Definition: mythuibuttonlist.h:48
VideoSelector::m_selectedList
QList< VideoInfo * > m_selectedList
Definition: videoselector.h:79
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
VideoSelector::m_currentParentalLevel
ParentalLevel::Level m_currentParentalLevel
Definition: videoselector.h:81
VideoSelector::getVideoListFromDB
static std::vector< VideoInfo * > * getVideoListFromDB(void)
Definition: videoselector.cpp:370
VideoSelector::updateSelectedList
void updateSelectedList(void)
Definition: videoselector.cpp:514
ParentalLevelChangeChecker
Definition: parentalcontrols.h:43
mythuibuttonlist.h
VideoSelector::m_parentalLevelChecker
ParentalLevelChangeChecker * m_parentalLevelChecker
Definition: videoselector.h:75
mythuiimage.h
MythUIButtonList::GetCount
int GetCount() const
Definition: mythuibuttonlist.cpp:1656
VideoSelector::titleChanged
void titleChanged(MythUIButtonListItem *item)
Definition: videoselector.cpp:192
VideoSelector::m_titleText
MythUIText * m_titleText
Definition: videoselector.h:89
VideoSelector::cancelPressed
void cancelPressed(void)
Definition: videoselector.cpp:313
VideoSelector::m_videoList
std::vector< VideoInfo * > * m_videoList
Definition: videoselector.h:78
MythScreenType::GetFocusWidget
MythUIType * GetFocusWidget(void) const
Definition: mythscreentype.cpp:113
VideoSelector::selectAll
void selectAll(void)
Definition: videoselector.cpp:152
VideoInfo::id
int id
Definition: videoselector.h:28
VideoSelector::setParentalLevel
void setParentalLevel(ParentalLevel::Level level)
Definition: videoselector.cpp:535
MythUIButtonListItem
Definition: mythuibuttonlist.h:41
VideoSelector::Create
bool Create() override
Definition: videoselector.cpp:45
ArchiveItem::type
QString type
Definition: archiveutil.h:53
VideoSelector::m_archiveList
QList< ArchiveItem * > * m_archiveList
Definition: videoselector.h:77
VideoSelector::keyPressEvent
bool keyPressEvent(QKeyEvent *e) override
Key event handler.
Definition: videoselector.cpp:94
VideoSelector::m_plText
MythUIText * m_plText
Definition: videoselector.h:83
MythUIType::Show
void Show(void)
Definition: mythuitype.cpp:1149
MythUIButtonList::itemClicked
void itemClicked(MythUIButtonListItem *item)
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1112
remotefile.h
ParentalLevel::Level
Level
Definition: parentalcontrols.h:12
archiveutil.h
MythScreenType::SetFocusWidget
bool SetFocusWidget(MythUIType *widget=nullptr)
Definition: mythscreentype.cpp:118
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:166
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:206
VideoSelector::parentalLevelChanged
void parentalLevelChanged(bool passwordValid, ParentalLevel::Level newLevel)
Definition: videoselector.cpp:540
videoselector.h
stringutil.h
VideoSelector::OKPressed
void OKPressed(void)
Definition: videoselector.cpp:241
VideoInfo
Definition: videoselector.h:26
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3660
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
ParentalLevelChangeChecker::SigResultReady
void SigResultReady(bool passwordValid, ParentalLevel::Level newLevel)
UIUtilDisp::Assign
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
VideoSelector::ShowMenu
void ShowMenu(void) override
Definition: videoselector.cpp:137
MythUIType::Hide
void Hide(void)
Definition: mythuitype.cpp:1144
StringUtil::formatKBytes
MBASE_PUBLIC QString formatKBytes(int64_t sizeKB, int prec=1)
Definition: stringutil.cpp:357
VideoSelector::m_coverImage
MythUIImage * m_coverImage
Definition: videoselector.h:92
VideoSelector::m_okButton
MythUIButton * m_okButton
Definition: videoselector.h:86
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:404
ParentalLevelChangeChecker::Check
void Check(ParentalLevel::Level fromLevel, ParentalLevel::Level toLevel)
Definition: parentalcontrols.cpp:366
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:687
MythUIButtonList::GetValue
virtual QString GetValue() const
Definition: mythuibuttonlist.cpp:1610
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:132
VideoSelector::m_cancelButton
MythUIButton * m_cancelButton
Definition: videoselector.h:87
MythUIButtonListItem::state
CheckState state() const
Definition: mythuibuttonlist.cpp:3614
mythcontext.h
MythUIButtonList::Reset
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuibuttonlist.cpp:116
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
StorageGroup
Definition: storagegroup.h:11
VideoSelector::m_filesizeText
MythUIText * m_filesizeText
Definition: videoselector.h:90
MythUIButtonList::SetItemCurrent
void SetItemCurrent(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1558
build_compdb.action
action
Definition: build_compdb.py:9
mythuibutton.h
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:323
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:838
VideoSelector::VideoSelector
VideoSelector(MythScreenStack *parent, QList< ArchiveItem * > *archiveList)
Definition: videoselector.cpp:27
ArchiveItem
Definition: archiveutil.h:50
VideoSelector::~VideoSelector
~VideoSelector(void) override
Definition: videoselector.cpp:36
MythUIImage::SetFilename
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
Definition: mythuiimage.cpp:674
MythUIButtonList::GetItemFirst
MythUIButtonListItem * GetItemFirst() const
Definition: mythuibuttonlist.cpp:1637
build_compdb.filename
filename
Definition: build_compdb.py:21
mythmainwindow.h
MythUIButtonListItem::setChecked
void setChecked(CheckState state)
Definition: mythuibuttonlist.cpp:3624
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:52
VideoSelector::updateVideoList
void updateVideoList(void)
Definition: videoselector.cpp:319
ShowOkPopup
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
Definition: mythdialogbox.cpp:562
VideoSelector::m_videoButtonList
MythUIButtonList * m_videoButtonList
Definition: videoselector.h:84
VideoSelector::getVideoList
void getVideoList(void)
Definition: videoselector.cpp:473
videoutils.h
MythUIButtonListItem::NotChecked
@ NotChecked
Definition: mythuibuttonlist.h:46
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838