MythTV  master
gallerythumbview.cpp
Go to the documentation of this file.
1 // C++
2 #include <chrono> // for milliseconds
3 #include <thread> // for sleep_for
4 #include <utility>
5 
6 // Qt
7 #include <QApplication>
8 
9 // MythTV
10 #include "libmythbase/compat.h"
12 #include "libmythbase/remotefile.h"
16 #include "libmythui/mythuitext.h"
17 
18 // MythFrontend
19 #include "galleryconfig.h"
20 #include "gallerythumbview.h"
21 
22 #define LOC QString("Thumbview: ")
23 
24 // EXIF tag 0x9286 UserComment can contain garbage
25 static QString clean_comment(const QString &comment)
26 {
27  QString result;
28  std::copy_if(comment.cbegin(), comment.cend(), std::back_inserter(result), [](QChar x) { return x.isPrint(); } );
29  return result;
30 }
31 
33 class ShellThread: public MThread
34 {
35 public:
36  ShellThread(QString cmd, QString path)
37  : MThread("Import"), m_command(std::move(cmd)), m_path(std::move(path)) {}
38 
39  int GetResult(void) const { return m_result; }
40 
41  void run() override // MThread
42  {
43  RunProlog();
44 
45  QString cmd = QString("cd %1 && %2").arg(m_path, m_command);
46  LOG(VB_GENERAL, LOG_INFO, QString("Executing \"%1\"").arg(cmd));
47 
48  m_result = myth_system(cmd);
49 
50  LOG(VB_GENERAL, LOG_INFO, QString(" ...with result %1").arg(m_result));
51 
52  RunEpilog();
53  }
54 
55 private:
56  int m_result {0};
57  QString m_command;
58  QString m_path;
59 };
60 
61 
63 class TransferThread : public MThread
64 {
65  Q_DECLARE_TR_FUNCTIONS(FileTransferWorker);
66 public:
67  using TransferMap = QMap<ImagePtrK, QString>;
68  using ImageSet = QSet<ImagePtrK>;
69 
70  TransferThread(TransferMap files, bool move, MythUIProgressDialog *dialog)
71  : MThread("FileTransfer"),
72  m_move(move), m_files(std::move(files)), m_dialog(dialog) {}
73 
74  ImageSet GetResult(void) { return m_failed; }
75 
76  void run() override // MThread
77  {
78  RunProlog();
79 
80  QString action = m_move ? tr("Moving") : tr("Copying");
81 
82  // Sum file sizes
83  auto keys = m_files.keys();
84  auto add_size = [](int t, const ImagePtrK & im){ return t + im->m_size; };
85  int total = std::accumulate(keys.cbegin(), keys.cend(), 0, add_size);
86 
87  int progressSize = 0;
88  for (auto it = m_files.constKeyValueBegin();
89  it != m_files.constKeyValueEnd(); it++)
90  {
91  const ImagePtrK & im = it->first;
92  QString newPath = it->second;
93  // Update progress dialog
94  if (m_dialog)
95  {
96  QString message = QString("%1 %2\n%3")
97  .arg(action, QFileInfo(im->m_url).fileName(),
98  ImageAdapterBase::FormatSize(im->m_size / 1024));
99 
100  auto *pue = new ProgressUpdateEvent(progressSize, total, message);
101  QApplication::postEvent(m_dialog, pue);
102  }
103 
104  LOG(VB_FILE, LOG_INFO, QString("%2 %3 -> %4")
105  .arg(action, im->m_url, newPath));
106 
107  bool success = m_move ? RemoteFile::MoveFile(im->m_url, newPath)
108  : RemoteFile::CopyFile(im->m_url, newPath,
109  false, true);
110  if (!success)
111  {
112  // Flag failures
113  m_failed.insert(im);
114 
115  LOG(VB_GENERAL, LOG_ERR,
116  QString("%1: Failed to copy/move %2 -> %3")
117  .arg(objectName(), im->m_url, m_files[im]));
118  }
119 
120  progressSize += im->m_size;
121  }
122 
123  // Update progress dialog
124  if (m_dialog)
125  {
126  auto *pue =
127  new ProgressUpdateEvent(progressSize, total, tr("Complete"));
128  QApplication::postEvent(m_dialog, pue);
129  }
130 
131  RunEpilog();
132  }
133 
134 private:
135  bool m_move;
139 };
140 
141 
146 static void WaitUntilDone(MThread &worker)
147 {
148  worker.start();
149  while (!worker.isFinished())
150  {
151  std::this_thread::sleep_for(1ms);
152  QCoreApplication::processEvents();
153  }
154 }
155 
156 
163  : MythScreenType(parent, name),
164  m_popupStack(*GetMythMainWindow()->GetStack("popup stack")),
165  m_mgr(ImageManagerFe::getInstance()),
166  // This screen uses a single fixed view (Parent dir, ordered dirs, ordered images)
167  m_view(new DirectoryView(kOrdered)),
168  m_infoList(*this),
169  // Start in edit mode unless a password exists
170  m_editsAllowed(gCoreContext->GetSetting("GalleryPassword").isEmpty())
171 {
172  // Hide hidden when edits disallowed
173  if (!m_editsAllowed)
174  m_mgr.SetVisibility(false);
175 }
176 
177 
182 {
183  LOG(VB_GUI, LOG_DEBUG, LOC + "Exiting Gallery");
184  delete m_view;
185 }
186 
187 
192 {
193  LOG(VB_GUI, LOG_DEBUG, LOC + "Closing Gallery");
194 
196 
197  // Cleanup local devices
199 
200  // Cleanup view
201  m_view->Clear();
202 
204 }
205 
206 
211 {
212  if (!LoadWindowFromXML("image-ui.xml", "gallery", this))
213  return false;
214 
215  // Determine zoom levels supported by theme
216  // images0 must exist; images1, images2 etc. are optional and enable zoom
217  int zoom = 0;
218  MythUIButtonList *widget = nullptr;
219  do
220  {
221  QString name = QString("images%1").arg(zoom++);
222  widget = dynamic_cast<MythUIButtonList *>(this->GetChild(name));
223  if (widget)
224  {
225  m_zoomWidgets.append(widget);
226  widget->SetVisible(false);
227  }
228  }
229  while (widget);
230 
231  if (m_zoomWidgets.isEmpty())
232  {
233  LOG(VB_GENERAL, LOG_ERR, LOC + "Screen 'Gallery' is missing 'images0'");
234  return false;
235  }
236  LOG(VB_GUI, LOG_DEBUG, LOC + QString("Screen 'Gallery' found %1 zoom levels")
237  .arg(m_zoomWidgets.size()));
238 
239  // File details list is managed elsewhere
240  if (!m_infoList.Create(false))
241  {
242  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot load 'Info buttonlist'");
243  return false;
244  }
245 
246  UIUtilW::Assign(this, m_captionText, "caption");
247  UIUtilW::Assign(this, m_emptyText, "noimages");
248  UIUtilW::Assign(this, m_positionText, "position");
249  UIUtilW::Assign(this, m_crumbsText, "breadcrumbs");
250  UIUtilW::Assign(this, m_hideFilterText, "hidefilter");
251  UIUtilW::Assign(this, m_typeFilterText, "typefilter");
252  UIUtilW::Assign(this, m_scanProgressText, "scanprogresstext");
253  UIUtilW::Assign(this, m_scanProgressBar, "scanprogressbar");
254 
255  if (m_scanProgressText)
257  if (m_scanProgressBar)
259 
260  BuildFocusList();
261 
262  // Initialise list widget with appropriate zoom level for this theme.
263  m_zoomLevel = gCoreContext->GetNumSetting("GalleryZoomLevel", 0);
264  SelectZoomWidget(0);
265 
266  return true;
267 }
268 
269 
274 bool GalleryThumbView::keyPressEvent(QKeyEvent *event)
275 {
276  if (GetFocusWidget()->keyPressEvent(event))
277  return true;
278 
279  QStringList actions;
280  bool handled = GetMythMainWindow()->TranslateKeyPress("Images", event, actions);
281 
282  for (int i = 0; i < actions.size() && !handled; i++)
283  {
284  QString action = actions[i];
285  handled = true;
286 
287  if (action == "MENU")
288  MenuMain();
289  else if (action == "INFO")
290  ShowDetails();
291  else if (action == "ZOOMIN")
292  ZoomIn();
293  else if (action == "ZOOMOUT")
294  ZoomOut();
295  else if (action == "ROTRIGHT")
296  RotateCW();
297  else if (action == "ROTLEFT")
298  RotateCCW();
299  else if (action == "FLIPHORIZONTAL")
300  FlipHorizontal();
301  else if (action == "FLIPVERTICAL")
302  FlipVertical();
303  else if (action == "COVER")
304  {
305  ImagePtrK im = m_view->GetSelected();
306  if (m_editsAllowed && im)
307  {
308  if (im == m_view->GetParent())
309  {
310  // Reset dir
311  m_mgr.SetCover(im->m_id, 0);
312  }
313  else
314  {
315  // Set parent cover
316  m_mgr.SetCover(im->m_parentId, im->m_id);
317  }
318  }
319  }
320  else if (action == "PLAY")
321  Slideshow();
322  else if (action == "RECURSIVESHOW")
323  {
324  ImagePtrK im = m_view->GetSelected();
325  if (im && im->IsDirectory())
327  }
328  else if (action == "MARK")
329  {
330  ImagePtrK im = m_view->GetSelected();
331  if (m_editsAllowed && im && im != m_view->GetParent())
332  DoMarkItem(!m_view->IsMarked(im->m_id));
333  }
334  else if (action == "ESCAPE" && !GetMythMainWindow()->IsExitingToMain())
335  {
336  // Exit info list, if shown
337  handled = m_infoList.Hide();
338 
339  // Ascend the tree unless parent is root,
340  // or a device and multiple devices/imports exist
341  if (!handled)
342  {
343  ImagePtrK node = m_view->GetParent();
344  if (node && node->m_id != GALLERY_DB_ID
345  && (!node->IsDevice() || m_mgr.DeviceCount() > 0))
346  handled = DirSelectUp();
347  }
348  }
349  else
350  handled = false;
351  }
352 
353  if (!handled)
354  handled = MythScreenType::keyPressEvent(event);
355 
356  return handled;
357 }
358 
359 
364 void GalleryThumbView::customEvent(QEvent *event)
365 {
366 
367  if (event->type() == MythEvent::kMythEventMessage)
368  {
369  auto *me = dynamic_cast<MythEvent *>(event);
370  if (me == nullptr)
371  return;
372 
373  const QString& mesg = me->Message();
374  QStringList extra = me->ExtraDataList();
375 
376  // Internal messages contain a hostname. Ignore other FE messages
377  QStringList token = mesg.split(' ');
378  if (token.size() >= 2 && token[1] != gCoreContext->GetHostName())
379  return;
380 
381  if (token[0] == "IMAGE_METADATA")
382  {
383  int id = extra[0].toInt();
384  ImagePtrK selected = m_view->GetSelected();
385 
386  if (selected && selected->m_id == id)
387  m_infoList.Display(*selected, extra.mid(1));
388  }
389  else if (token[0] == "THUMB_AVAILABLE")
390  {
391  int id = extra[0].toInt();
392 
393  // Note existance of all thumbs
394  m_thumbExists.insert(id);
395 
396  // Get all buttons waiting for this thumbnail
397  QList<ThumbLocation> affected = m_pendingMap.values(id);
398 
399  // Only concerned with thumbnails we've requested
400  if (affected.isEmpty())
401  return;
402 
403  LOG(VB_GENERAL, LOG_DEBUG, LOC +
404  QString("Rx %1 : %2").arg(token[0], extra.join(",")));
405 
406  // Thumb url was cached when request was sent
407  QString url = m_view->GetCachedThumbUrl(id);
408 
409  // Set thumbnail for each button now it exists
410  for (const ThumbLocation & location : std::as_const(affected))
411  {
412  MythUIButtonListItem *button = location.first;
413  int index = location.second;
414 
415  auto im = button->GetData().value<ImagePtrK>();
416  if (im)
417  UpdateThumbnail(button, im, url, index);
418  }
419 
420  // Cancel pending request
421  m_pendingMap.remove(id);
422  }
423  else if (token[0] == "IMAGE_DB_CHANGED")
424  {
425  // Expects csv list of deleted ids, csv list of changed ids
426  LOG(VB_GENERAL, LOG_DEBUG, LOC +
427  QString("Rx %1 : %2").arg(token[0], extra.join(",")));
428 
429  if (!extra.isEmpty())
430  {
431  QStringList idDeleted =
432  extra[0].split(",", Qt::SkipEmptyParts);
433  RemoveImages(idDeleted);
434  }
435  if (extra.size() >= 2)
436  {
437  QStringList idChanged =
438  extra[1].split(",", Qt::SkipEmptyParts);
439  RemoveImages(idChanged, false);
440  }
441 
442  // Refresh display
444  }
445  else if (token[0] == "IMAGE_DEVICE_CHANGED")
446  {
447  // Expects list of url prefixes
448  LOG(VB_GENERAL, LOG_DEBUG, LOC +
449  QString("Rx %1 : %2").arg(token[0], extra.join(",")));
450 
451  // Clear everything. Local devices will be rebuilt
452  m_view->Clear();
453  m_thumbExists.clear();
454 
455  // Remove thumbs & images from image cache using supplied prefixes
456  for (const QString & url : std::as_const(extra))
458 
459  // Refresh display
461  }
462  else if (token[0] == "IMAGE_SCAN_STATUS" && extra.size() == 3)
463  {
464  // Expects scanner id, scanned#, total#
465  UpdateScanProgress(extra[0], extra[1].toInt(), extra[2].toInt());
466  }
467  }
468  else if (event->type() == DialogCompletionEvent::kEventType)
469  {
470  auto *dce = (DialogCompletionEvent *)(event);
471 
472  QString resultid = dce->GetId();
473  int buttonnum = dce->GetResult();
474 
475  if (resultid == "FileRename")
476  {
477  QString newName = dce->GetResultText();
479  {
480  QString err = m_mgr.RenameFile(m_menuState.m_selected,
481  newName);
482  if (!err.isEmpty())
483  ShowOkPopup(err);
484  }
485  }
486  else if (resultid == "MakeDir")
487  {
489  {
490  // Prohibit subtrees
491  QString name = dce->GetResultText();
492  QString err = name.contains("/")
493  ? tr("Invalid Name")
495  QStringList(name));
496  if (!err.isEmpty())
497  ShowOkPopup(err);
498  }
499  }
500  else if (resultid == "SlideOrderMenu")
501  {
502  SlideOrderType slideOrder = kOrdered;
503 
504  switch (buttonnum)
505  {
506  case 0: slideOrder = kOrdered; break;
507  case 1: slideOrder = kShuffle; break;
508  case 2: slideOrder = kRandom; break;
509  case 3: slideOrder = kSeasonal; break;
510  }
511  gCoreContext->SaveSetting("GallerySlideOrder", slideOrder);
512  LOG(VB_FILE, LOG_DEBUG, LOC + QString("Order %1").arg(slideOrder));
513  }
514  else if (resultid == "ImageCaptionMenu")
515  {
516  ImageCaptionType captions = kNoCaption;
517 
518  switch (buttonnum)
519  {
520  case 0: captions = kNameCaption; break;
521  case 1: captions = kDateCaption; break;
522  case 2: captions = kUserCaption; break;
523  case 3: captions = kNoCaption; break;
524  }
525  gCoreContext->SaveSetting("GalleryImageCaption", captions);
526  BuildImageList();
527  }
528  else if (resultid == "DirCaptionMenu")
529  {
530  ImageCaptionType captions = kNoCaption;
531 
532  switch (buttonnum)
533  {
534  case 0: captions = kNameCaption; break;
535  case 1: captions = kDateCaption; break;
536  case 2: captions = kNoCaption; break;
537  }
538  gCoreContext->SaveSetting("GalleryDirCaption", captions);
539  BuildImageList();
540  }
541  else if (resultid == "Password")
542  {
543  QString password = dce->GetResultText();
544  m_editsAllowed = (password == gCoreContext->GetSetting("GalleryPassword"));
545  }
546  else if (buttonnum == 1)
547  {
548  // Confirm current file deletion
549  QString err;
550  if (resultid == "ConfirmDelete" && m_menuState.m_selected)
551  {
553  err = m_mgr.DeleteFiles(ids);
554  }
555  // Confirm marked file deletion
556  else if (resultid == "ConfirmDeleteMarked")
557  {
559  }
560  else
561  return;
562 
563  if (!err.isEmpty())
564  ShowOkPopup(err);
565  }
566  }
567 }
568 
569 
575 void GalleryThumbView::RemoveImages(const QStringList &ids, bool deleted)
576 {
577  for (const QString & id : std::as_const(ids))
578  {
579  // Remove image from view
580  QStringList urls = m_view->RemoveImage(id.toInt(), deleted);
581  // Cleanup url lookup
582  m_thumbExists.remove(id.toInt());
583 
584  // Remove thumbs & images from image cache
585  for (const QString & url : std::as_const(urls))
586  {
587  LOG(VB_FILE, LOG_DEBUG, LOC +
588  QString("Clearing image cache of '%1'").arg(url));
589 
591  }
592  }
593 }
594 
595 
600 {
601  // Detect any running BE scans
602  // Expects OK, scanner id, current#, total#
603  QStringList message = ImageManagerFe::ScanQuery();
604  if (message.size() == 4 && message[0] == "OK")
605  {
606  UpdateScanProgress(message[1], message[2].toInt(), message[3].toInt());
607  }
608 
609  // Only receive events after device/scan status has been established
610  gCoreContext->addListener(this);
611 
612  // Start at Root if devices exist. Otherwise go straight to SG node
614 
615  LoadData(start);
616 }
617 
618 
624 {
626 
627  // Load view for parent directory
628  if (m_view->LoadFromDb(parent))
629  {
630  m_imageList->SetVisible(true);
631  if (m_emptyText)
632  {
633  m_emptyText->SetVisible(false);
634  m_emptyText->Reset();
635  }
636 
637  // Construct the buttonlist
638  BuildImageList();
639  }
640  else
641  {
642  m_infoList.Hide();
643  m_imageList->SetVisible(false);
644  if (m_emptyText)
645  {
646  m_emptyText->SetVisible(true);
647  m_emptyText->SetText(tr("No images found.\n"
648  "Scan storage group using menu,\n"
649  "or insert/mount local media.\n"));
650  }
651  }
652 }
653 
654 
659 {
660  m_imageList->Reset();
661  m_pendingMap.clear();
662 
663  // Get parent & all children
664  ImageListK nodes = m_view->GetAllNodes();
665  ImagePtrK selected = m_view->GetSelected();
666 
667  // go through the entire list and update
668  for (const ImagePtrK & im : std::as_const(nodes))
669  {
670  if (im)
671  {
672  // Data must be set by constructor: First item is automatically
673  // selected and must have data available for selection event, as
674  // subsequent reselection of same item will always fail.
675  auto *item = new MythUIButtonListItem(m_imageList, "",
676  QVariant::fromValue(im));
677 
678  item->setCheckable(true);
679  item->setChecked(MythUIButtonListItem::NotChecked);
680 
681  // assign and display all information about
682  // the current item, like title and subdirectory count
683  UpdateImageItem(item);
684 
685  // Treat parent differently
686  if (im == nodes[0])
687  {
688  // Only non-root parents can ascend
689  if (im->m_id != GALLERY_DB_ID)
690  item->DisplayState("upfolder", "parenttype");
691  }
692  else if (im == selected)
693  // Reinstate the active button item. Note this would fail for parent
695  }
696  }
697 }
698 
699 
705 {
706  auto im = item->GetData().value<ImagePtrK >();
707  if (!im)
708  return;
709 
710  // Allow themes to distinguish between roots, folders, pics, videos
711  switch (im->m_type)
712  {
713  case kDevice:
714  case kCloneDir:
715  case kDirectory:
716  if (im->m_dirCount > 0)
717  {
718  item->SetText(QString("%1/%2")
719  .arg(im->m_fileCount).arg(im->m_dirCount),
720  "childcount");
721  }
722  else
723  {
724  item->SetText(QString::number(im->m_fileCount), "childcount");
725  }
726 
727  item->DisplayState(im->IsDevice() ? "device" : "subfolder", "buttontype");
728  break;
729 
730  case kImageFile:
731  item->DisplayState("image", "buttontype");
732  break;
733 
734  case kVideoFile:
735  item->DisplayState("video", "buttontype");
736  break;
737 
738  default:
739  break;
740  }
741 
742  // Allow theme to distinguish visible/hidden nodes
743  QString hideState = (im->m_isHidden) ? "hidden" : "visible";
744  item->DisplayState(hideState, "buttonstate");
745 
746  // Caption
747  QString text;
749  im->IsFile() ? "GalleryImageCaption"
750  : "GalleryDirCaption");
751  switch (show)
752  {
753  case kNameCaption: text = m_mgr.CrumbName(*im); break;
754  case kDateCaption: text = m_mgr.ShortDateOf(im); break;
755  case kUserCaption: text = clean_comment(im->m_comment); break;
756  default:
757  case kNoCaption: text = ""; break;
758  }
759  item->SetText(text);
760 
761  // Set marked state
763  = m_view->IsMarked(im->m_id)
766 
767  item->setChecked(state);
768 
769  // Thumbnails required
770  ImageIdList request;
771 
772  if (im->m_thumbNails.size() == 1)
773  {
774  // Single thumbnail
775  QString url = CheckThumbnail(item, im, request, 0);
776 
777  if (!url.isEmpty())
778  UpdateThumbnail(item, im, url, 0);
779  }
780  else
781  {
782  // Dir showing up to 4 thumbs. Set them all at same time
783  InfoMap thumbMap;
784  for (int index = 0; index < im->m_thumbNails.size(); ++index)
785  {
786  QString url = CheckThumbnail(item, im, request, index);
787  if (!url.isEmpty())
788  thumbMap.insert(QString("thumbimage%1").arg(index), url);
789  }
790  if (!thumbMap.isEmpty())
791  item->SetImageFromMap(thumbMap);
792  }
793 
794  // Request creation/verification of unknown thumbnails.
795  if (!request.isEmpty())
796  m_mgr.CreateThumbnails(request, im->IsDirectory());
797 }
798 
799 
812  ImageIdList &request, int index)
813 {
814  ThumbPair thumb(im->m_thumbNails.at(index));
815  int id = thumb.first;
816 
817  if (m_thumbExists.contains(id))
818  return thumb.second;
819 
820  // Request BE thumbnail check if it is not already pending
821  if (!m_pendingMap.contains(id))
822  request << id;
823 
824  // Note this button is awaiting an update
825  m_pendingMap.insert(id, qMakePair(item, index));
826 
827  return "";
828 }
829 
830 
839  const ImagePtrK& im, const QString &url,
840  int index)
841 {
842  if (im->m_thumbNails.size() == 1)
843  {
844  // Pics, dirs & videos use separate widgets
845  switch (im->m_type)
846  {
847  case kImageFile: button->SetImage(url); break;
848  case kVideoFile: button->SetImage(url, "videoimage"); break;
849  default: button->SetImage(url, "folderimage"); break;
850  }
851  }
852  else
853  // Dir with 4 thumbnails
854  button->SetImage(url, QString("thumbimage%1").arg(index));
855 }
856 
857 
865 void GalleryThumbView::UpdateScanProgress(const QString &scanner,
866  int current, int total)
867 {
868  // Scan update
869  m_scanProgress.insert(scanner, qMakePair(current, total));
870 
871  // Detect end of this scan
872  if (current >= total)
873  {
874  LOG(VB_GUI, LOG_DEBUG, LOC + QString("Scan Finished %1 %2/%3")
875  .arg(scanner).arg(current).arg(total));
876 
877  // Mark inactive scanner
878  m_scanActive.remove(scanner);
879 
880  // Detect end of last scan
881  if (m_scanActive.isEmpty())
882  {
883  if (m_scanProgressText)
884  {
887  }
888  if (m_scanProgressBar)
889  {
892  }
893 
894  m_scanProgress.clear();
895 
896  return;
897  }
898  }
899  else
900  {
901  // Detect first scan update
902  if (m_scanActive.isEmpty())
903  {
904  // Show progressbar when first scan starts
905  if (m_scanProgressBar)
906  {
909  }
910  if (m_scanProgressText)
912  }
913 
914  if (!m_scanActive.contains(scanner))
915  {
916  LOG(VB_GUI, LOG_DEBUG, LOC + QString("Scan Started %1 %2/%3")
917  .arg(scanner).arg(current).arg(total));
918 
919  // Mark active scanner
920  m_scanActive.insert(scanner);
921  }
922  }
923 
924  // Aggregate all running scans
925  int currentAgg = 0;
926  int totalAgg = 0;
927  for (IntPair scan : std::as_const(m_scanProgress))
928  {
929  currentAgg += scan.first;
930  totalAgg += scan.second;
931  }
932 
933  if (m_scanProgressBar)
934  {
935  m_scanProgressBar->SetUsed(currentAgg);
936  m_scanProgressBar->SetTotal(totalAgg);
937  }
938  if (m_scanProgressText)
939  m_scanProgressText->SetText(tr("%L1 of %L3").arg(currentAgg).arg(totalAgg));
940 }
941 
942 
947 {
948  if (m_positionText)
950 
951  if (m_captionText)
952  m_captionText->Reset();
953 
954  if (m_crumbsText)
955  m_crumbsText->Reset();
956 
957  if (m_hideFilterText)
959 
960  if (m_typeFilterText)
962 }
963 
964 
970 {
971  auto im = item->GetData().value<ImagePtrK >();
972  if (im)
973  {
974  // update the position in the node list
975  m_view->Select(im->m_id);
976 
977  // show the name/path of the image
978  if (m_crumbsText)
979  m_crumbsText->SetText(m_mgr.CrumbName(*im, true));
980 
981  if (m_captionText)
982  {
983  // show the date & comment of non-root nodes
984  QStringList text;
985  if (im->m_id != GALLERY_DB_ID)
986  {
987  if (im->IsFile() || im->IsDevice())
988  text << ImageManagerFe::LongDateOf(im);
989 
990  QString comment = clean_comment(im->m_comment);
991  if (!comment.isEmpty())
992  text << comment;
993  }
994  m_captionText->SetText(text.join(" - "));
995  }
996 
997  if (m_hideFilterText)
998  {
999  m_hideFilterText->SetText(m_mgr.GetVisibility() ? tr("Hidden") : "");
1000  }
1001 
1002  if (m_typeFilterText)
1003  {
1004  QString text = "";
1005  switch (m_mgr.GetType())
1006  {
1007  case kPicAndVideo : text = ""; break;
1008  case kPicOnly : text = tr("Pictures"); break;
1009  case kVideoOnly : text = tr("Videos"); break;
1010  }
1011  m_typeFilterText->SetText(text);
1012  }
1013 
1014  // show the position of the image
1015  if (m_positionText)
1017 
1018  // Update any file details information
1019  m_infoList.Update(im);
1020  }
1021 }
1022 
1023 
1028 {
1029  // Create the main menu
1030  auto *menu = new MythMenu(tr("Gallery Options"), this, "mainmenu");
1031 
1032  // Menu options depend on the marked files and the current node
1034 
1035  if (m_menuState.m_selected)
1036  {
1037  if (m_editsAllowed)
1038  {
1039  MenuMarked(menu);
1040  MenuPaste(menu);
1042  MenuAction(menu);
1043  }
1045  MenuShow(menu);
1046  if (!m_editsAllowed)
1047  menu->AddItem(tr("Enable Edits"), &GalleryThumbView::ShowPassword);
1048  }
1049 
1050  // Depends on current status of backend scanner - string(number(isBackend()))
1051  if (m_scanActive.contains("1"))
1052  menu->AddItem(tr("Stop Scan"), &GalleryThumbView::StopScan);
1053  else
1054  menu->AddItem(tr("Scan Storage Group"), &GalleryThumbView::StartScan);
1055 
1056  menu->AddItem(tr("Settings"), &GalleryThumbView::ShowSettings);
1057 
1058  auto *popup = new MythDialogBox(menu, &m_popupStack, "menuPopup");
1059  if (popup->Create())
1060  m_popupStack.AddScreen(popup);
1061  else
1062  delete popup;
1063 }
1064 
1065 
1071 {
1072  ImagePtrK parent = m_view->GetParent();
1073 
1074  if (m_menuState.m_childCount == 0 || parent.isNull())
1075  return;
1076 
1077  QString title = tr("%L1 marked").arg(m_menuState.m_markedId.size());
1078  auto *menu = new MythMenu(title, this, "markmenu");
1079 
1080  // Mark/unmark selected
1081  if (m_menuState.m_selected->IsFile())
1082  {
1084  menu->AddItem(tr("Unmark File"), &GalleryThumbView::UnmarkItem);
1085  else
1086  menu->AddItem(tr("Mark File"), &GalleryThumbView::MarkItem);
1087  }
1088  // Cannot mark/unmark parent dir from this level
1089  else if (!m_menuState.m_selected->IsDevice()
1090  && m_menuState.m_selected != parent)
1091  {
1093  menu->AddItem(tr("Unmark Directory"), &GalleryThumbView::UnmarkItem);
1094  else
1095  menu->AddItem(tr("Mark Directory"), &GalleryThumbView::MarkItem);
1096  }
1097 
1098  if (parent->m_id != GALLERY_DB_ID)
1099  {
1100  // Mark All if unmarked files exist
1102  menu->AddItem(tr("Mark All"), &GalleryThumbView::MarkAll);
1103 
1104  // Unmark All if marked files exist
1105  if (!m_menuState.m_markedId.isEmpty())
1106  {
1107  menu->AddItem(tr("Unmark All"), &GalleryThumbView::UnmarkAll);
1108  menu->AddItem(tr("Invert Marked"), &GalleryThumbView::MarkInvertAll);
1109  }
1110  }
1111 
1112  if (menu->IsEmpty())
1113  delete menu;
1114  else
1115  mainMenu->AddItem(tr("Mark"), nullptr, menu);
1116 }
1117 
1118 
1124 {
1125  // Can only copy/move into non-root dirs
1126  if (m_menuState.m_selected->IsDirectory()
1127  && m_menuState.m_selected->m_id != GALLERY_DB_ID)
1128  {
1129  // Operate on current marked files, if any
1131  if (files.isEmpty())
1132  files = m_menuState.m_prevMarkedId;
1133  if (files.isEmpty())
1134  return;
1135 
1136  QString title = tr("%L1 marked").arg(files.size());
1137 
1138  auto *menu = new MythMenu(title, this, "pastemenu");
1139 
1140  menu->AddItem(tr("Move Marked Into"), &GalleryThumbView::Move);
1141  menu->AddItem(tr("Copy Marked Into"), qOverload<>(&GalleryThumbView::Copy));
1142 
1143  mainMenu->AddItem(tr("Paste"), nullptr, menu);
1144  }
1145 }
1146 
1147 
1153 {
1154  // Operate on marked files, if any, otherwise selected node
1155  if (!m_menuState.m_markedId.isEmpty())
1156  {
1157  QString title = tr("%L1 marked").arg(m_menuState.m_markedId.size());
1158 
1159  auto *menu = new MythMenu(title, this, "");
1160 
1161  menu->AddItem(tr("Rotate Marked CW"), &GalleryThumbView::RotateCWMarked);
1162  menu->AddItem(tr("Rotate Marked CCW"), &GalleryThumbView::RotateCCWMarked);
1163  menu->AddItem(tr("Flip Marked Horizontal"), &GalleryThumbView::FlipHorizontalMarked);
1164  menu->AddItem(tr("Flip Marked Vertical"), &GalleryThumbView::FlipVerticalMarked);
1165  menu->AddItem(tr("Reset Marked to Exif"), &GalleryThumbView::ResetExifMarked);
1166 
1167  mainMenu->AddItem(tr("Transforms"), nullptr, menu);
1168  }
1169  else if (m_menuState.m_selected->IsFile())
1170  {
1171  auto *menu = new MythMenu(m_menuState.m_selected->m_baseName, this, "");
1172 
1173  menu->AddItem(tr("Rotate CW"), &GalleryThumbView::RotateCW);
1174  menu->AddItem(tr("Rotate CCW"), &GalleryThumbView::RotateCCW);
1175  menu->AddItem(tr("Flip Horizontal"), &GalleryThumbView::FlipHorizontal);
1176  menu->AddItem(tr("Flip Vertical"), &GalleryThumbView::FlipVertical);
1177  menu->AddItem(tr("Reset to Exif"), &GalleryThumbView::ResetExif);
1178 
1179  mainMenu->AddItem(tr("Transforms"), nullptr, menu);
1180  }
1181 }
1182 
1183 
1189 {
1190  MythMenu *menu = nullptr;
1191  ImagePtrK selected = m_menuState.m_selected;
1192 
1193  // Operate on current marked files, if any
1194  if (!m_menuState.m_markedId.empty())
1195  {
1196  QString title = tr("%L1 marked").arg(m_menuState.m_markedId.size());
1197 
1198  menu = new MythMenu(title, this, "actionmenu");
1199 
1200  // Only offer Hide/Unhide if relevant
1202  menu->AddItem(tr("Hide Marked"), &GalleryThumbView::HideMarked);
1204  menu->AddItem(tr("Unhide Marked"), &GalleryThumbView::UnhideMarked);
1205 
1206  menu->AddItem(tr("Delete Marked"), &GalleryThumbView::DeleteMarked);
1207  }
1208  else
1209  {
1210  // Operate on selected file/dir
1211  menu = new MythMenu(selected->m_baseName, this, "actionmenu");
1212 
1213  // Prohibit actions on devices and parent dirs
1214  if (!selected->IsDevice() && selected != m_view->GetParent())
1215  {
1216  if (selected->m_isHidden)
1217  menu->AddItem(tr("Unhide"), &GalleryThumbView::Unhide);
1218  else
1219  menu->AddItem(tr("Hide"), &GalleryThumbView::HideItem);
1220 
1221  menu->AddItem(tr("Use as Cover"), &GalleryThumbView::SetCover);
1222  menu->AddItem(tr("Delete"), &GalleryThumbView::DeleteItem);
1223  menu->AddItem(tr("Rename"), &GalleryThumbView::ShowRenameInput);
1224  }
1225  else if (selected->m_userThumbnail)
1226  menu->AddItem(tr("Reset Cover"), &GalleryThumbView::ResetCover);
1227  }
1228 
1229  // Can only mkdir in a non-root dir
1230  if (selected->IsDirectory()
1231  && selected->m_id != GALLERY_DB_ID)
1232  menu->AddItem(tr("Create Directory"), &GalleryThumbView::MakeDir);
1233 
1234  // Only show import command on root, when defined
1235  if (selected->m_id == GALLERY_DB_ID
1236  && !gCoreContext->GetSetting("GalleryImportCmd").isEmpty())
1237  menu->AddItem(tr("Import"), &GalleryThumbView::Import);
1238 
1239  // Only show eject when devices (excluding import) exist
1240  if (selected->IsDevice() && selected->IsLocal())
1241  menu->AddItem(tr("Eject media"), &GalleryThumbView::Eject);
1242 
1243  if (menu->IsEmpty())
1244  delete menu;
1245  else
1246  mainMenu->AddItem(tr("Actions"), nullptr, menu);
1247 }
1248 
1249 
1255 {
1256  int order = gCoreContext->GetNumSetting("GallerySlideOrder", kOrdered);
1257 
1258  QString ordering;
1259  switch (order)
1260  {
1261  case kShuffle : ordering = tr("Shuffled"); break;
1262  case kRandom : ordering = tr("Random"); break;
1263  case kSeasonal : ordering = tr("Seasonal"); break;
1264  default:
1265  case kOrdered : ordering = tr("Ordered"); break;
1266  }
1267 
1268  auto *menu = new MythMenu(tr("Slideshow") + " (" + ordering + ")",
1269  this, "SlideshowMenu");
1270 
1271  // Use selected dir or parent, if image selected
1272  if (m_menuState.m_selected->IsDirectory())
1273  {
1274  if (m_menuState.m_selected->m_fileCount > 0)
1275  menu->AddItem(tr("Directory"), &GalleryThumbView::Slideshow);
1276 
1277  if (m_menuState.m_selected->m_dirCount > 0)
1278  menu->AddItem(tr("Recursive"), &GalleryThumbView::RecursiveSlideshow);
1279  }
1280  else
1281  menu->AddItem(tr("Current Directory"), &GalleryThumbView::Slideshow);
1282 
1283  auto *orderMenu = new MythMenu(tr("Slideshow Order"), this, "SlideOrderMenu");
1284 
1285  orderMenu->AddItem(tr("Ordered"), nullptr, nullptr, order == kOrdered);
1286  orderMenu->AddItem(tr("Shuffled"), nullptr, nullptr, order == kShuffle);
1287  orderMenu->AddItem(tr("Random"), nullptr, nullptr, order == kRandom);
1288  orderMenu->AddItem(tr("Seasonal"), nullptr, nullptr, order == kSeasonal);
1289 
1290  menu->AddItem(tr("Change Order"), nullptr, orderMenu);
1291 
1292  if (gCoreContext->GetBoolSetting("GalleryRepeat", false))
1293  menu->AddItem(tr("Turn Repeat Off"), &GalleryThumbView::RepeatOff);
1294  else
1295  menu->AddItem(tr("Turn Repeat On"), &GalleryThumbView::RepeatOn);
1296 
1297  mainMenu->AddItem(tr("Slideshow"), nullptr, menu);
1298 }
1299 
1300 
1306 {
1307  auto *menu = new MythMenu(tr("Show Options"), this, "showmenu");
1308 
1309  int type = m_mgr.GetType();
1310  if (type == kPicAndVideo)
1311  {
1312  menu->AddItem(tr("Hide Pictures"), &GalleryThumbView::HidePictures);
1313  menu->AddItem(tr("Hide Videos"), &GalleryThumbView::HideVideos);
1314  }
1315  else
1316  menu->AddItem(type == kPicOnly ? tr("Show Videos") : tr("Show Pictures"),
1318 
1319  int show = gCoreContext->GetNumSetting("GalleryImageCaption");
1320  auto *captionMenu = new MythMenu(tr("Image Captions"), this,
1321  "ImageCaptionMenu");
1322 
1323  captionMenu->AddItem(tr("Name"), nullptr, nullptr, show == kNameCaption);
1324  captionMenu->AddItem(tr("Date"), nullptr, nullptr, show == kDateCaption);
1325  captionMenu->AddItem(tr("Comment"), nullptr, nullptr, show == kUserCaption);
1326  captionMenu->AddItem(tr("None"), nullptr, nullptr, show == kNoCaption);
1327 
1328  menu->AddItem(tr("Image Captions"), nullptr, captionMenu);
1329 
1330  show = gCoreContext->GetNumSetting("GalleryDirCaption");
1331  captionMenu = new MythMenu(tr("Directory Captions"), this, "DirCaptionMenu");
1332 
1333  captionMenu->AddItem(tr("Name"), nullptr, nullptr, show == kNameCaption);
1334  captionMenu->AddItem(tr("Date"), nullptr, nullptr, show == kDateCaption);
1335  captionMenu->AddItem(tr("None"), nullptr, nullptr, show == kNoCaption);
1336 
1337  menu->AddItem(tr("Directory Captions"), nullptr, captionMenu);
1338 
1339  if (m_editsAllowed)
1340  {
1341  if (m_mgr.GetVisibility())
1342  menu->AddItem(tr("Hide Hidden Items"), &GalleryThumbView::HideHidden);
1343  else
1344  menu->AddItem(tr("Show Hidden Items"), &GalleryThumbView::ShowHidden);
1345  }
1346 
1347  if (m_zoomLevel > 0)
1348  menu->AddItem(tr("Zoom Out"), &GalleryThumbView::ZoomOut);
1349  if (m_zoomLevel < m_zoomWidgets.size() - 1)
1350  menu->AddItem(tr("Zoom In"), &GalleryThumbView::ZoomIn);
1351 
1352  QString details = m_infoList.GetState() == kNoInfo
1353  ? tr("Show Details") : tr("Hide Details");
1354 
1355  menu->AddItem(details, &GalleryThumbView::ShowDetails);
1356 
1357  mainMenu->AddItem(tr("Show"), nullptr, menu);
1358 }
1359 
1360 
1366 {
1367  // Only update selection if image is currently displayed
1368  if (m_view->Select(id, -1))
1369  BuildImageList();
1370 }
1371 
1372 
1378 {
1379  if (!item)
1380  return;
1381 
1382  auto im = item->GetData().value<ImagePtrK>();
1383  if (!im)
1384  return;
1385 
1386  switch (im->m_type)
1387  {
1388  case kDevice:
1389  case kCloneDir:
1390  case kDirectory:
1391  if (im == m_view->GetParent())
1392  DirSelectUp();
1393  else
1394  DirSelectDown();
1395  break;
1396 
1397  case kImageFile:
1398  case kVideoFile:
1399  StartSlideshow(kBrowseSlides); break;
1400  };
1401 }
1402 
1403 
1409 {
1410  QString err = m_mgr.ScanImagesAction(start);
1411  if (!err.isEmpty())
1412  ShowOkPopup(err);
1413 }
1414 
1415 
1421 {
1422  ImagePtrK selected = m_view->GetSelected();
1423  if (!selected)
1424  return;
1425 
1427  auto *slide = new GallerySlideView(mainStack, "galleryslideview",
1428  m_editsAllowed);
1429  if (slide->Create())
1430  {
1431  mainStack->AddScreen(slide);
1432 
1433  // Update selected item when slideshow exits
1434  connect(slide, &GallerySlideView::ImageSelected,
1436 
1437  if (selected->IsDirectory())
1438  {
1439  // Show selected dir
1440  slide->Start(mode, selected->m_id);
1441  }
1442  else
1443  {
1444  // Show current dir starting at selection
1445  slide->Start(mode, selected->m_parentId, selected->m_id);
1446  }
1447  }
1448  else
1449  delete slide;
1450 }
1451 
1452 
1457 {
1458  ImagePtrK im = m_view->GetParent();
1459  if (im)
1460  {
1461  LOG(VB_GUI, LOG_DEBUG, LOC +
1462  QString("Going up from %1").arg(im->m_filePath));
1463 
1464  // Select the upfolder in the higher dir
1465  m_view->Select(im->m_id);
1466 
1467  // Create tree rooted at parent of the kUpFolder directory node
1468  LoadData(im->m_parentId);
1469  }
1470  return true;
1471 }
1472 
1473 
1478 {
1479  ImagePtrK im = m_view->GetSelected();
1480  if (im)
1481  {
1482  LOG(VB_GUI, LOG_DEBUG, LOC +
1483  QString("Going down to %1").arg(im->m_filePath));
1484 
1485  // Create tree rooted at selected item
1486  LoadData(im->m_id);
1487  }
1488 }
1489 
1490 
1496 {
1497  ImagePtrK im = m_view->GetSelected();
1498  if (im)
1499  {
1500  // Mark/unmark selected item
1501  m_view->Mark(im->m_id, mark);
1502 
1503  // Redisplay buttonlist as a parent dir may have been unmarked
1504  BuildImageList();
1505  }
1506 }
1507 
1508 
1514 {
1515  if (mark)
1516  m_view->MarkAll();
1517  else
1518  m_view->ClearMarked();
1519 
1520  // Redisplay buttonlist
1521  BuildImageList();
1522 }
1523 
1524 
1529 {
1530  m_view->InvertMarked();
1531 
1532  // Redisplay buttonlist
1533  BuildImageList();
1534 }
1535 
1536 
1542 {
1543  ImagePtrK im = m_view->GetSelected();
1544  if (im && m_editsAllowed)
1545  {
1546  ImageIdList ids;
1547  ids.append(im->m_id);
1548  QString err = m_mgr.ChangeOrientation(transform, ids);
1549  if (!err.isEmpty())
1550  ShowOkPopup(err);
1551  }
1552 }
1553 
1554 
1560 {
1561  QString err = m_mgr.ChangeOrientation(transform, m_menuState.m_markedId);
1562  if (!err.isEmpty())
1563  ShowOkPopup(err);
1564 }
1565 
1566 
1572 {
1573  if (m_menuState.m_selected)
1574  {
1575  ImageIdList ids;
1576  ids.append(m_menuState.m_selected->m_id);
1577 
1578  QString err = m_mgr.HideFiles(hide, ids);
1579  if (!err.isEmpty())
1580  {
1581  ShowOkPopup(err);
1582  }
1583  else if (hide && !m_mgr.GetVisibility())
1584  {
1585  // Unmark invisible file
1586  m_view->Mark(m_menuState.m_selected->m_id, false);
1587  }
1588  }
1589 }
1590 
1591 
1597 {
1598  QString err = m_mgr.HideFiles(hide, m_menuState.m_markedId);
1599  if (!err.isEmpty())
1600  {
1601  ShowOkPopup(err);
1602  }
1603  else if (hide && !m_mgr.GetVisibility())
1604  {
1605  // Unmark invisible files
1606  for (int id : std::as_const(m_menuState.m_markedId))
1607  m_view->Mark(id, false);
1608  }
1609 }
1610 
1611 
1616 {
1617  if (m_menuState.m_selected)
1618  ShowDialog(tr("Do you want to delete\n%1 ?")
1619  .arg(m_menuState.m_selected->m_baseName), "ConfirmDelete");
1620 }
1621 
1622 
1627 {
1628  ShowDialog(tr("Do you want to delete all marked files ?"),
1629  "ConfirmDeleteMarked");
1630 }
1631 
1632 
1637 {
1638  // Show settings dialog
1639  auto *config = new GallerySettings(m_editsAllowed);
1641  auto *ssd = new StandardSettingDialog(mainStack, "gallerysettings", config);
1642  if (!ssd->Create())
1643  {
1644  delete ssd;
1645  return;
1646  }
1647 
1648  mainStack->AddScreen(ssd);
1649 
1650  // Effect setting changes when dialog saves on exit
1651 
1652  connect(config, &GallerySettings::ClearDbPressed,
1654 
1655  connect(config, &GallerySettings::OrderChanged,
1656  this, [this]()
1657  {
1658  // Update db view, reset cover cache & reload
1659  int sortIm = gCoreContext->GetNumSetting("GalleryImageOrder");
1660  int sortDir = gCoreContext->GetNumSetting("GalleryDirOrder");
1661  m_mgr.SetSortOrder(sortIm, sortDir);
1662  m_view->ClearCache();
1664  });
1665 
1666  connect(config, &GallerySettings::DateChanged,
1667  this, [this]()
1668  {
1669  QString date = gCoreContext->GetSetting("GalleryDateFormat");
1670  m_mgr.SetDateFormat(date);
1671  BuildImageList();
1672  });
1673 
1674  connect(config, &GallerySettings::ExclusionsChanged,
1675  this, [this]()
1676  {
1677  // Request rescan
1678  QString exclusions = gCoreContext->GetSetting("GalleryIgnoreFilter");
1679  m_view->ClearCache();
1680  ImageManagerFe::IgnoreDirs(exclusions);
1681  });
1682 }
1683 
1684 
1690 {
1691  gCoreContext->SaveBoolSetting("GalleryShowHidden", show);
1692 
1693  // Update Db(s)
1695 
1696  // Reset dir thumbnail cache
1697  m_view->ClearCache();;
1698 
1700 }
1701 
1702 
1708 void GalleryThumbView::ShowDialog(const QString& msg, const QString& event)
1709 {
1710  auto *popup = new MythConfirmationDialog(&m_popupStack, msg, true);
1711 
1712  if (popup->Create())
1713  {
1714  popup->SetReturnEvent(this, event);
1715  m_popupStack.AddScreen(popup);
1716  }
1717  else
1718  delete popup;
1719 }
1720 
1721 
1726 {
1727  if (m_menuState.m_selected)
1728  {
1729  QString base = QFileInfo(m_menuState.m_selected->m_baseName).completeBaseName();
1730  QString msg = tr("Enter a new name:");
1731  auto *popup = new MythTextInputDialog(&m_popupStack, msg, FilterNone,
1732  false, base);
1733  if (popup->Create())
1734  {
1735  popup->SetReturnEvent(this, "FileRename");
1736  m_popupStack.AddScreen(popup);
1737  }
1738  else
1739  delete popup;
1740  }
1741 }
1742 
1743 
1748 {
1750 }
1751 
1752 
1757 {
1758  QString msg = tr("Enter password:");
1759  auto *popup = new MythTextInputDialog(&m_popupStack, msg, FilterNone, true);
1760  if (popup->Create())
1761  {
1762  popup->SetReturnEvent(this, "Password");
1763  m_popupStack.AddScreen(popup);
1764  }
1765  else
1766  delete popup;
1767 }
1768 
1769 
1774 {
1775  gCoreContext->SaveSetting("GalleryShowType", type);
1776 
1777  // Update Db(s)
1778  m_mgr.SetType(type);
1779 
1780  // Reset dir thumbnail cache
1781  m_view->ClearCache();
1782 
1784 }
1785 
1786 
1792 {
1793  if (m_menuState.m_selected)
1794  {
1795  QString err = reset ? m_mgr.SetCover(m_menuState.m_selected->m_id, 0)
1796  : m_mgr.SetCover(m_menuState.m_selected->m_parentId,
1797  m_menuState.m_selected->m_id);
1798  if (!err.isEmpty())
1799  ShowOkPopup(err);
1800  }
1801 }
1802 
1803 
1808 {
1809  SelectZoomWidget(-1);
1810  BuildImageList();
1811 }
1812 
1813 
1818 {
1819  SelectZoomWidget(1);
1820  BuildImageList();
1821 }
1822 
1823 
1829 {
1830  m_zoomLevel += change;
1831 
1832  // constrain to zoom levels supported by theme
1833  if (m_zoomLevel < 0)
1834  m_zoomLevel = 0;
1835  if (m_zoomLevel >= m_zoomWidgets.size())
1836  m_zoomLevel = m_zoomWidgets.size() - 1;
1837 
1838  // Store any requested change, but not constraining adjustments
1839  // Thus, changing to a theme with fewer zoom levels will not overwrite the
1840  // setting
1841  if (change != 0)
1842  gCoreContext->SaveSetting("GalleryZoomLevel", m_zoomLevel);
1843 
1844  // dump the current list widget
1845  if (m_imageList)
1846  {
1847  m_imageList->SetVisible(false);
1848  disconnect(m_imageList, nullptr, this, nullptr);
1849  }
1850 
1851  // initialise new list widget
1853 
1854  m_imageList->SetVisible(true);
1856 
1857  // Monitor list actions (after focus events have been ignored)
1862 }
1863 
1864 
1869 {
1870  auto *popup = new MythTextInputDialog(&m_popupStack,
1871  tr("Enter name of new directory"),
1872  FilterNone, false);
1873  if (popup->Create())
1874  {
1875  popup->SetReturnEvent(this, "MakeDir");
1876  m_popupStack.AddScreen(popup);
1877  }
1878  else
1879  delete popup;
1880 }
1881 
1882 
1887 {
1889  if (dir)
1890  m_mgr.CloseDevices(dir->m_device, true);
1891 }
1892 
1893 
1903 void GalleryThumbView::Copy(bool deleteAfter)
1904 {
1905  // Destination must be a dir
1906  ImagePtrK destDir = m_menuState.m_selected;
1907  if (!destDir || destDir->IsFile())
1908  return;
1909 
1910  // Use current markings, if any. Otherwise use previous markings
1911  ImageIdList markedIds = m_menuState.m_markedId;
1912  if (markedIds.isEmpty())
1913  {
1914  markedIds = m_menuState.m_prevMarkedId;
1915  if (markedIds.isEmpty())
1916  {
1917  ShowOkPopup(tr("No files specified"));
1918  return;
1919  }
1920  }
1921 
1922  // Get all files/dirs in subtree(s). Only files are copied
1923  ImageList files;
1924  ImageList dirs;
1925  m_mgr.GetDescendants(markedIds, files, dirs);
1926 
1927  if (dirs.isEmpty() && files.isEmpty())
1928  {
1929  ShowOkPopup(tr("No images"));
1930  // Nothing to clean up
1931  return;
1932  }
1933 
1934  // Child dirs appear before their subdirs. If no dirs, images are all direct children
1935  ImagePtrK aChild = dirs.isEmpty() ? files[0] : dirs[0];
1936 
1937  // Determine parent path including trailing /
1938  int basePathSize = aChild->m_filePath.size() - aChild->m_baseName.size();
1939 
1940  // Update filepaths for Db & generate URLs for filesystem copy
1941  // Only copy files, destination dirs will be created automatically
1942  TransferThread::TransferMap transfers;
1943  for (const ImagePtr & im : std::as_const(files))
1944  {
1945  // Replace base path with destination path
1946  im->m_filePath = ImageManagerFe::ConstructPath(destDir->m_filePath,
1947  im->m_filePath.mid(basePathSize));
1948 
1949  transfers.insert(im, m_mgr.BuildTransferUrl(im->m_filePath,
1950  destDir->IsLocal()));
1951  }
1952 
1953  // Create progress dialog
1954  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1955  auto *progress = new MythUIProgressDialog(tr("Copying files"), popupStack,
1956  "copydialog");
1957  if (progress->Create())
1958  popupStack->AddScreen(progress, false);
1959  else
1960  {
1961  delete progress;
1962  progress = nullptr;
1963  }
1964 
1965  // Copy files in a servant thread
1966  TransferThread copy(transfers, false, progress);
1968  TransferThread::ImageSet failed = copy.GetResult();
1969 
1970  if (progress)
1971  progress->Close();
1972 
1973  if (!failed.isEmpty())
1974  ShowOkPopup(tr("Failed to copy %L1/%Ln file(s)", nullptr, transfers.size())
1975  .arg(failed.size()));
1976 
1977  // Don't update Db for files that failed
1978  for (const ImagePtrK & im : std::as_const(failed))
1979  transfers.remove(im);
1980 
1981  ImageListK newImages = transfers.keys();
1982 
1983  // Include dirs
1984  QStringList dirPaths;
1985  for (const ImagePtr & im : std::as_const(dirs))
1986  {
1987  QString relPath = im->m_filePath.mid(basePathSize);
1988 
1989  dirPaths << relPath;
1990 
1991  // Replace base path with destination path
1992  im->m_filePath = ImageManagerFe::ConstructPath(destDir->m_filePath, relPath);
1993 
1994  // Append dirs so that hidden state & cover is preserved for new dirs
1995  // Pre-existing dirs will take precedance over these.
1996  newImages.append(im);
1997  }
1998 
1999  // Copy empty dirs as well (will fail for non-empty dirs)
2000  if (!dirPaths.isEmpty())
2001  m_mgr.MakeDir(destDir->m_id, dirPaths, false);
2002 
2003  if (!newImages.isEmpty())
2004  {
2005  // Update Db
2006  m_mgr.CreateImages(destDir->m_id, newImages);
2007 
2008  if (deleteAfter)
2009  {
2010  // Delete files/dirs that have been successfully copied
2011  // Will fail for dirs containing images that failed to copy
2012  ImageIdList ids;
2013  for (const ImagePtrK & im : std::as_const(newImages))
2014  ids << im->m_id;
2015 
2016  m_mgr.DeleteFiles(ids);
2017  }
2018  }
2019 }
2020 
2021 
2032 {
2033  // Destination must be a dir
2034  ImagePtrK destDir = m_menuState.m_selected;
2035  if (!destDir || destDir->IsFile())
2036  return;
2037 
2038  // Use current markings, if any. Otherwise use previous markings
2039  ImageIdList markedIds = m_menuState.m_markedId;
2040  if (markedIds.isEmpty())
2041  {
2042  markedIds = m_menuState.m_prevMarkedId;
2043  if (markedIds.isEmpty())
2044  {
2045  ShowOkPopup(tr("No files specified"));
2046  return;
2047  }
2048  }
2049 
2050  // Note UI mandates that transferees are either all local or all remote
2051  if (destDir->IsLocal() != ImageItem::IsLocalId(markedIds[0]))
2052  {
2053  // Moves between hosts require copy/delete
2054  Copy(true);
2055  return;
2056  }
2057 
2058  // Get marked images. Each file and dir will be renamed
2059  ImageList files;
2060  ImageList dirs;
2061  if (m_mgr.GetImages(markedIds, files, dirs) <= 0)
2062  {
2063  ShowOkPopup(tr("No images specified"));
2064  // Nothing to clean up
2065  return;
2066  }
2067  ImageList images = dirs + files;
2068 
2069  // Determine parent from first dir or pic
2070  ImagePtr aChild = images[0];
2071 
2072  // Determine parent path including trailing /
2073  // Note UI mandates that transferees all have same parent.
2074  int basePathSize = aChild->m_filePath.size() - aChild->m_baseName.size();
2075  QString parentPath = aChild->m_filePath.left(basePathSize);
2076 
2077  // Determine destination URLs
2078  TransferThread::TransferMap transfers;
2079  for (const QSharedPointer<ImageItem> & im : std::as_const(images))
2080  {
2081  // Replace base path with destination path
2082  QString newPath = ImageManagerFe::ConstructPath(destDir->m_filePath,
2083  im->m_filePath.mid(basePathSize));
2084 
2085  transfers.insert(im, m_mgr.BuildTransferUrl(newPath, aChild->IsLocal()));
2086  }
2087 
2088  // Create progress dialog
2089  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
2090  auto *progress = new MythUIProgressDialog(tr("Moving files"), popupStack,
2091  "movedialog");
2092 
2093  if (progress->Create())
2094  popupStack->AddScreen(progress, false);
2095  else
2096  {
2097  delete progress;
2098  progress = nullptr;
2099  }
2100 
2101  // Move files in a servant thread
2102  TransferThread move(transfers, true, progress);
2103  WaitUntilDone(move);
2104  TransferThread::ImageSet failed = move.GetResult();
2105 
2106  if (progress)
2107  progress->Close();
2108 
2109  if (!failed.isEmpty())
2110  ShowOkPopup(tr("Failed to move %L1/%Ln file(s)", nullptr, transfers.size())
2111  .arg(failed.size()));
2112 
2113  // Don't update Db for files that failed
2114  for (const ImagePtrK & im : std::as_const(failed))
2115  transfers.remove(im);
2116 
2117  if (!transfers.isEmpty())
2118  {
2119  ImageListK moved = transfers.keys();
2120 
2121  // Unmark moved files
2122  for (const ImagePtrK & im : std::as_const(moved))
2123  m_view->Mark(im->m_id, false);
2124 
2125  // Update Db
2126  m_mgr.MoveDbImages(destDir, moved, parentPath);
2127  }
2128 }
2129 
2130 
2135 {
2136  QString path = m_mgr.CreateImport();
2137  if (path.isEmpty())
2138  {
2139  ShowOkPopup(tr("Failed to create temporary directory."));
2140  return;
2141  }
2142 
2143  // Replace placeholder in command
2144  QString cmd = gCoreContext->GetSetting("GalleryImportCmd");
2145  cmd.replace("%TMPDIR%", path);
2146 
2147  // Run command in a separate thread
2148  MythUIBusyDialog *busy =
2149  ShowBusyPopup(tr("Running Import command.\nPlease wait..."));
2150 
2151  ShellThread thread(cmd, path);
2152  WaitUntilDone(thread);
2153 
2154  if (busy)
2155  busy->Close();
2156 
2157  int error = thread.GetResult();
2158  if (error != 0)
2159  ShowOkPopup(tr("Import command failed.\nError: %1").arg(error));
2160 
2161  // Rescan local devices
2162  QString err = m_mgr.ScanImagesAction(true, true);
2163  if (!err.isEmpty())
2164  LOG(VB_GENERAL, LOG_ERR, LOC + err);
2165 }
GalleryThumbView::FlipVerticalMarked
void FlipVerticalMarked()
Definition: gallerythumbview.h:81
GalleryThumbView::UpdateThumbnail
static void UpdateThumbnail(MythUIButtonListItem *button, const ImagePtrK &im, const QString &url, int index)
Update the buttonlist item with a thumbnail.
Definition: gallerythumbview.cpp:838
kPicAndVideo
@ kPicAndVideo
Show Pictures & Videos.
Definition: imagemanager.h:78
ImagePtrK
QSharedPointer< ImageItemK > ImagePtrK
Definition: imagetypes.h:165
MythMainWindow::GetMainStack
MythScreenStack * GetMainStack()
Definition: mythmainwindow.cpp:318
MenuSubjects::m_prevMarkedId
ImageIdList m_prevMarkedId
Ids of marked items in previous dir.
Definition: galleryviews.h:69
InfoList::Display
void Display(ImageItemK &im, const QStringList &tagStrings)
Build list of metadata tags.
Definition: galleryinfo.cpp:222
ImageDbReader::GetImages
int GetImages(const ImageIdList &ids, ImageList &files, ImageList &dirs) const
Returns images (local or remote but not a combination)
Definition: imagemanager.cpp:1832
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
ShowBusyPopup
MythUIBusyDialog * ShowBusyPopup(const QString &message)
Definition: mythprogressdialog.cpp:95
GalleryThumbView::m_scanProgressBar
MythUIProgressBar * m_scanProgressBar
Definition: gallerythumbview.h:138
GalleryThumbView::m_thumbExists
QSet< int > m_thumbExists
Images where thumbnails are known to exist.
Definition: gallerythumbview.h:161
GalleryThumbView::IntPair
QPair< int, int > IntPair
Definition: gallerythumbview.h:127
mythuitext.h
mythuiprogressbar.h
ImageAdapterBase::ConstructPath
static QString ConstructPath(const QString &path, const QString &name)
Assembles a canonical file path without corrupting its absolute/relative nature.
Definition: imagemanager.h:132
MythEvent::kMythEventMessage
static const Type kMythEventMessage
Definition: mythevent.h:79
FilterNone
@ FilterNone
Definition: mythuitextedit.h:21
ImageManagerFe::CreateThumbnails
void CreateThumbnails(const ImageIdList &ids, bool forFolder)
Create thumbnails or verify that they already exist.
Definition: imagemanager.cpp:1978
TransferThread::m_failed
ImageSet m_failed
Definition: gallerythumbview.cpp:137
GalleryThumbView::m_positionText
MythUIText * m_positionText
Definition: gallerythumbview.h:136
DirectoryView::GetPosition
QString GetPosition() const
Get positional status.
Definition: galleryviews.cpp:484
GallerySettings::DateChanged
void DateChanged()
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
error
static void error(const char *str,...)
Definition: vbi.cpp:36
kVideoFile
@ kVideoFile
A video.
Definition: imagetypes.h:40
GallerySettings::ExclusionsChanged
void ExclusionsChanged()
GalleryThumbView::StartScan
void StartScan()
Definition: gallerythumbview.h:111
TransferThread::Q_DECLARE_TR_FUNCTIONS
Q_DECLARE_TR_FUNCTIONS(FileTransferWorker)
GalleryThumbView::HideMarked
void HideMarked()
Definition: gallerythumbview.h:94
ImageSlideShowType
ImageSlideShowType
Type of slide show.
Definition: galleryslideview.h:15
GalleryThumbView::MenuPaste
void MenuPaste(MythMenu *mainMenu)
Add a Paste submenu.
Definition: gallerythumbview.cpp:1123
GalleryThumbView::m_imageList
MythUIButtonList * m_imageList
Definition: gallerythumbview.h:130
MythUIButtonListItem::DisplayState
void DisplayState(const QString &state, const QString &name)
Definition: mythuibuttonlist.cpp:3554
ImageManagerFe::CreateImages
QString CreateImages(int destId, const ImageListK &images)
Copies database images (but not the files themselves).
Definition: imagemanager.cpp:2224
GalleryThumbView::MakeDir
void MakeDir()
Show dialog to input new directory name.
Definition: gallerythumbview.cpp:1868
GalleryThumbView::SelectZoomWidget
void SelectZoomWidget(int change)
Change buttonlist to use a different size.
Definition: gallerythumbview.cpp:1828
ImageManagerFe::ScanImagesAction
QString ScanImagesAction(bool start, bool local=false)
Handle scanner start/stop commands.
Definition: imagemanager.cpp:2013
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:386
MythUIButtonListItem::SetImageFromMap
void SetImageFromMap(const InfoMap &imageMap)
Definition: mythuibuttonlist.cpp:3452
ImageManagerFe::BuildTransferUrl
QString BuildTransferUrl(const QString &path, bool local) const
Generate Myth URL for a local or remote path.
Definition: imagemanager.h:499
GalleryThumbView::m_scanProgress
QHash< QString, IntPair > m_scanProgress
Last scan updates received from scanners.
Definition: gallerythumbview.h:150
progress
bool progress
Definition: mythcommflag.cpp:69
GalleryThumbView::customEvent
void customEvent(QEvent *event) override
Handle custom events.
Definition: gallerythumbview.cpp:364
MythUIProgressBar::SetStart
void SetStart(int value)
Definition: mythuiprogressbar.cpp:63
GalleryThumbView::DirSelectDown
void DirSelectDown()
Goes one directory level down.
Definition: gallerythumbview.cpp:1477
GallerySettings
Definition: galleryconfig.h:9
MenuSubjects::m_unhiddenMarked
bool m_unhiddenMarked
Is any marked item unhidden ?
Definition: galleryviews.h:72
GalleryThumbView::UnmarkAll
void UnmarkAll()
Definition: gallerythumbview.h:88
DirectoryView::Mark
void Mark(int id, bool mark)
Mark/unmark an image/dir.
Definition: galleryviews.cpp:736
GalleryThumbView::m_infoList
InfoList m_infoList
Image details overlay.
Definition: gallerythumbview.h:147
ImageManagerFe::MakeDir
QString MakeDir(int parent, const QStringList &names, bool rescan=true)
Create directories.
Definition: imagemanager.cpp:2182
MythUIType::GetChild
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:133
MenuSubjects::m_markedId
ImageIdList m_markedId
Ids of all marked items.
Definition: galleryviews.h:68
kDirectory
@ kDirectory
A device sub directory.
Definition: imagetypes.h:38
GalleryThumbView::ShowPassword
void ShowPassword()
Displays dialog to accept password.
Definition: gallerythumbview.cpp:1756
kBrowseSlides
@ kBrowseSlides
Definition: galleryslideview.h:16
ImageManagerFe::DeviceCount
int DeviceCount() const
Definition: imagemanager.h:100
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
GalleryThumbView::FlipHorizontalMarked
void FlipHorizontalMarked()
Definition: gallerythumbview.h:80
MythMenu::AddItem
void AddItem(const QString &title)
Definition: mythdialogbox.h:110
ImageManagerFe::IgnoreDirs
static QString IgnoreDirs(const QString &excludes)
Set directories to ignore during scans of the storage group.
Definition: imagemanager.cpp:2166
MythUIProgressBar::SetUsed
void SetUsed(int value)
Definition: mythuiprogressbar.cpp:69
GalleryThumbView::GalleryThumbView
GalleryThumbView(MythScreenStack *parent, const char *name)
Constructor.
Definition: gallerythumbview.cpp:162
mythdialogbox.h
MythScreenStack
Definition: mythscreenstack.h:16
GalleryThumbView::FlipVertical
void FlipVertical()
Definition: gallerythumbview.h:76
MythUIButtonListItem::FullChecked
@ FullChecked
Definition: mythuibuttonlist.h:48
GalleryThumbView::LoadData
void LoadData(int parent)
Loads & displays images from database.
Definition: gallerythumbview.cpp:623
TransferThread::m_files
TransferMap m_files
Maps source filepath to destination filepath.
Definition: gallerythumbview.cpp:136
TransferThread::run
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: gallerythumbview.cpp:76
kNoInfo
@ kNoInfo
Details not displayed.
Definition: galleryinfo.h:16
kDateCaption
@ kDateCaption
Dates.
Definition: gallerythumbview.h:17
GalleryThumbView::m_crumbsText
MythUIText * m_crumbsText
Definition: gallerythumbview.h:132
InfoList::Update
void Update(const ImagePtrK &im)
Populates available exif details for the current image/dir.
Definition: galleryinfo.cpp:196
kNoCaption
@ kNoCaption
None.
Definition: gallerythumbview.h:15
GalleryThumbView::m_typeFilterText
MythUIText * m_typeFilterText
Definition: gallerythumbview.h:135
ImageManagerFe::HideFiles
QString HideFiles(bool hidden, const ImageIdList &ids)
Hide/unhide images.
Definition: imagemanager.cpp:2059
GalleryThumbView::MenuAction
void MenuAction(MythMenu *mainMenu)
Add a Action submenu.
Definition: gallerythumbview.cpp:1188
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MThread::RunProlog
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
MenuSubjects::m_childCount
ssize_t m_childCount
Number of images & dirs excl parent.
Definition: galleryviews.h:70
ImageManagerFe::SetDateFormat
void SetDateFormat(const QString &format)
Definition: imagemanager.h:492
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
show
static void show(uint8_t *buf, int length)
Definition: ringbuffer.cpp:339
kSeasonal
@ kSeasonal
Biased random selection so that images are more likely to appear on anniversaries.
Definition: galleryviews.h:25
GalleryThumbView::m_hideFilterText
MythUIText * m_hideFilterText
Definition: gallerythumbview.h:134
GalleryThumbView::Import
void Import()
Executes user 'Import command'.
Definition: gallerythumbview.cpp:2134
DirectoryView::GetParent
ImagePtrK GetParent() const
Definition: galleryviews.h:175
myth_system
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
Definition: mythsystemlegacy.cpp:506
ImageDbReader::SetVisibility
void SetVisibility(bool showHidden)
Definition: imagemanager.h:417
GalleryThumbView::ZoomIn
void ZoomIn()
Use smaller buttonlist widgets.
Definition: gallerythumbview.cpp:1817
GalleryThumbView::HideHidden
void HideHidden()
Definition: gallerythumbview.h:99
GalleryThumbView::DoHideItem
void DoHideItem(bool hide=true)
Hide or unhide item.
Definition: gallerythumbview.cpp:1571
TransferThread::TransferThread
TransferThread(TransferMap files, bool move, MythUIProgressDialog *dialog)
Definition: gallerythumbview.cpp:70
GalleryThumbView::m_menuState
MenuSubjects m_menuState
Current selection/marked files when menu is invoked.
Definition: gallerythumbview.h:155
TransferThread::TransferMap
QMap< ImagePtrK, QString > TransferMap
Definition: gallerythumbview.cpp:67
ImageManagerFe::CloseDevices
void CloseDevices(int devId=DEVICE_INVALID, bool eject=false)
Definition: imagemanager.cpp:2405
FlatView::Select
bool Select(int id, int fallback=0)
Selects first occurrence of an image.
Definition: galleryviews.cpp:119
hardwareprofile.scan.scan
def scan(profile, smoonURL, gate)
Definition: scan.py:55
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:14
mythprogressdialog.h
MythEvent::Message
const QString & Message() const
Definition: mythevent.h:65
MenuSubjects::m_selectedMarked
bool m_selectedMarked
Is selected item marked ?
Definition: galleryviews.h:67
MythUIButtonListItem::CheckState
CheckState
Definition: mythuibuttonlist.h:44
GalleryThumbView::Create
bool Create() override
Initialises and shows the graphical elements.
Definition: gallerythumbview.cpp:210
ImageManagerFe::ChangeOrientation
QString ChangeOrientation(ImageFileTransform transform, const ImageIdList &ids)
Apply an orientation transform to images.
Definition: imagemanager.cpp:2090
MythScreenType::GetFocusWidget
MythUIType * GetFocusWidget(void) const
Definition: mythscreentype.cpp:113
GalleryThumbView::MenuMain
void MenuMain()
Shows the main menu when the MENU button was pressed.
Definition: gallerythumbview.cpp:1027
mythsystemlegacy.h
DirectoryView::MarkAll
void MarkAll()
Mark all images/dirs.
Definition: galleryviews.cpp:723
InfoMap
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
GalleryThumbView::HideItem
void HideItem()
Definition: gallerythumbview.h:91
MythUIButtonListItem::SetText
void SetText(const QString &text, const QString &name="", const QString &state="")
Definition: mythuibuttonlist.cpp:3264
GalleryThumbView::RepeatOff
static void RepeatOff()
Definition: gallerythumbview.h:124
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
GalleryThumbView::SetUiSelection
void SetUiSelection(MythUIButtonListItem *item)
Updates text widgets for selected item.
Definition: gallerythumbview.cpp:969
GalleryThumbView::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Handle keypresses.
Definition: gallerythumbview.cpp:274
GalleryThumbView::DoSetCover
void DoSetCover(bool reset=false)
Set or reset thumbnails to use for a directory cover.
Definition: gallerythumbview.cpp:1791
MythUIButtonListItem
Definition: mythuibuttonlist.h:41
TransferThread
Worker thread for copying/moving files.
Definition: gallerythumbview.cpp:63
ImageFileTransform
ImageFileTransform
Image transformations.
Definition: imagemetadata.h:46
TransferThread::m_move
bool m_move
Copy if false, Move if true.
Definition: gallerythumbview.cpp:135
ProgressUpdateEvent
Definition: mythprogressdialog.h:16
StandardSettingDialog
Definition: standardsettings.h:468
MythUIProgressBar::SetTotal
void SetTotal(int value)
Definition: mythuiprogressbar.cpp:81
ShellThread
Worker thread for running import.
Definition: gallerythumbview.cpp:33
GalleryThumbView::ResetUiSelection
void ResetUiSelection()
Clears all text widgets for selected item.
Definition: gallerythumbview.cpp:946
GallerySlideView
Slideshow screen.
Definition: galleryslideview.h:23
InfoList::Toggle
void Toggle(const ImagePtrK &im)
Toggle infolist state for an image. Focusable widgets toggle between Basic & Full info....
Definition: galleryinfo.cpp:85
GalleryThumbView::RotateCCWMarked
void RotateCCWMarked()
Definition: gallerythumbview.h:79
GalleryThumbView::MenuShow
void MenuShow(MythMenu *mainMenu)
Add a Show submenu.
Definition: gallerythumbview.cpp:1305
kPicOnly
@ kPicOnly
Hide videos.
Definition: imagemanager.h:79
GalleryThumbView::ShowSettings
void ShowSettings()
Show configuration screen.
Definition: gallerythumbview.cpp:1636
MythUIButtonList::itemClicked
void itemClicked(MythUIButtonListItem *item)
ImageDbReader::SetSortOrder
void SetSortOrder(int order, int dirOrder)
Definition: imagemanager.h:414
GalleryThumbView::UpdateScanProgress
void UpdateScanProgress(const QString &scanner, int current, int total)
Update progressbar with scan status.
Definition: gallerythumbview.cpp:865
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
MythFile::copy
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
Definition: mythmiscutil.cpp:263
MythUIProgressBar::Reset
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuiprogressbar.cpp:16
MythUIProgressDialog
Definition: mythprogressdialog.h:59
MythScreenType::SetFocusWidget
bool SetFocusWidget(MythUIType *widget=nullptr)
Definition: mythscreentype.cpp:118
hardwareprofile.i18n.t
t
Definition: i18n.py:36
GalleryThumbView::TransformMarked
void TransformMarked(ImageFileTransform tran=kRotateCW)
Apply transform to marked images.
Definition: gallerythumbview.cpp:1559
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:166
menu
static MythThemedMenu * menu
Definition: mythtv-setup.cpp:58
GalleryThumbView::SelectImage
void SelectImage(int id)
Select item if it is displayed.
Definition: gallerythumbview.cpp:1365
compat.h
GalleryThumbView::ResetCover
void ResetCover()
Definition: gallerythumbview.h:102
GalleryThumbView::DoShowHidden
void DoShowHidden(bool show=true)
Show or hide hidden files.
Definition: gallerythumbview.cpp:1689
mark
Definition: lang.cpp:22
ImageDbReader::GetType
int GetType() const
Definition: imagemanager.h:408
ImageAdapterBase::FormatSize
static QString FormatSize(int sizeKib)
Definition: imagemanager.h:143
GalleryThumbView::m_view
DirectoryView * m_view
List of images comprising the view.
Definition: gallerythumbview.h:146
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:206
gallerythumbview.h
Implements Gallery Thumbnail screen.
GalleryThumbView::MenuSlideshow
void MenuSlideshow(MythMenu *mainMenu)
Add a Slideshow submenu.
Definition: gallerythumbview.cpp:1254
kShuffle
@ kShuffle
Each image appears exactly once, but in random order.
Definition: galleryviews.h:23
GalleryThumbView::ClearSgDb
static void ClearSgDb()
Definition: gallerythumbview.h:34
GalleryThumbView::BuildImageList
void BuildImageList()
Displays all images in current view.
Definition: gallerythumbview.cpp:658
ImageDbReader::GetDescendants
void GetDescendants(const ImageIdList &ids, ImageList &files, ImageList &dirs) const
Return all (local or remote) images that are direct children of a dir.
Definition: imagemanager.cpp:1872
GalleryThumbView::StartSlideshow
void StartSlideshow(ImageSlideShowType mode)
Start slideshow screen.
Definition: gallerythumbview.cpp:1420
GalleryThumbView::ShowRenameInput
void ShowRenameInput()
Show dialog to allow input.
Definition: gallerythumbview.cpp:1725
GalleryThumbView::ShowDialog
void ShowDialog(const QString &msg, const QString &event="")
Show a confirmation dialog.
Definition: gallerythumbview.cpp:1708
FlatView::GetAllNodes
ImageListK GetAllNodes() const
Get all images/dirs in view.
Definition: galleryviews.cpp:53
DirectoryView::InvertMarked
void InvertMarked()
Mark all unmarked items, unmark all marked items.
Definition: galleryviews.cpp:755
MythUIThemeCache::RemoveFromCacheByFile
void RemoveFromCacheByFile(const QString &File)
Definition: mythuithemecache.cpp:508
GalleryThumbView::DeleteMarked
void DeleteMarked()
Confirm user deletion of marked files.
Definition: gallerythumbview.cpp:1626
GalleryThumbView::Unhide
void Unhide()
Definition: gallerythumbview.h:92
ImageListK
QList< ImagePtrK > ImageListK
Definition: imagetypes.h:166
MThread::RunEpilog
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
ImageCaptionType
ImageCaptionType
Type of captions to display.
Definition: gallerythumbview.h:14
GalleryThumbView::m_scanActive
QSet< QString > m_scanActive
Scanners currently scanning.
Definition: gallerythumbview.h:152
FlatView::GetCachedThumbUrl
QString GetCachedThumbUrl(int id) const
Definition: galleryviews.h:121
GalleryThumbView::RotateCCW
void RotateCCW()
Definition: gallerythumbview.h:74
ImageManagerFe::LongDateOf
static QString LongDateOf(const ImagePtrK &im)
Return a timestamp/datestamp for an image or dir.
Definition: imagemanager.cpp:2325
ShellThread::m_command
QString m_command
Definition: gallerythumbview.cpp:57
MythUIBusyDialog
Definition: mythprogressdialog.h:36
ImageDbReader::GetVisibility
bool GetVisibility() const
Definition: imagemanager.h:409
GalleryThumbView::ZoomOut
void ZoomOut()
Use larger buttonlist widgets.
Definition: gallerythumbview.cpp:1807
MThread::isFinished
bool isFinished(void) const
Definition: mthread.cpp:258
GalleryThumbView::ResetExif
void ResetExif()
Definition: gallerythumbview.h:77
FlatView::GetSelected
ImagePtrK GetSelected() const
Get current selection.
Definition: galleryviews.cpp:66
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3656
ImageManagerFe::SetCover
QString SetCover(int parent, int cover)
Set image to use as a cover thumbnail(s)
Definition: imagemanager.cpp:2122
GallerySettings::ClearDbPressed
void ClearDbPressed()
GalleryThumbView::ItemClicked
void ItemClicked(MythUIButtonListItem *item)
Action item click.
Definition: gallerythumbview.cpp:1377
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
InfoList::GetState
InfoVisibleState GetState() const
Definition: galleryinfo.h:33
ImageItem::IsLocalId
static bool IsLocalId(int id)
Determine image type (local/remote) from its id. Root/Gallery is remote.
Definition: imagetypes.h:122
LOC
#define LOC
Definition: gallerythumbview.cpp:22
GallerySettings::OrderChanged
void OrderChanged()
GalleryThumbView::DoHideMarked
void DoHideMarked(bool hide=true)
Hide or unhide marked items.
Definition: gallerythumbview.cpp:1596
GalleryThumbView::DirSelectUp
bool DirSelectUp()
Goes up one directory level.
Definition: gallerythumbview.cpp:1456
GalleryThumbView::UpdateImageItem
void UpdateImageItem(MythUIButtonListItem *item)
Initialises a single buttonlist item.
Definition: gallerythumbview.cpp:704
TransferThread::GetResult
ImageSet GetResult(void)
Definition: gallerythumbview.cpp:74
GalleryThumbView::ShowDetails
void ShowDetails()
Shows exif info/details about an item.
Definition: gallerythumbview.cpp:1747
GalleryThumbView::CheckThumbnail
QString CheckThumbnail(MythUIButtonListItem *item, const ImagePtrK &im, ImageIdList &request, int index)
Verify thumbnail is known to exist.
Definition: gallerythumbview.cpp:811
RemoteFile::CopyFile
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:580
GalleryThumbView::FlipHorizontal
void FlipHorizontal()
Definition: gallerythumbview.h:75
GalleryThumbView::m_popupStack
MythScreenStack & m_popupStack
Definition: gallerythumbview.h:144
GalleryThumbView::m_pendingMap
QMultiHash< int, ThumbLocation > m_pendingMap
Buttons waiting for thumbnails to be created.
Definition: gallerythumbview.h:159
ImageList
QVector< ImagePtr > ImageList
Definition: imagetypes.h:160
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:912
GalleryThumbView::TransformItem
void TransformItem(ImageFileTransform tran=kRotateCW)
Apply transform to an image.
Definition: gallerythumbview.cpp:1541
ImageManagerFe::CreateImport
QString CreateImport()
Definition: imagemanager.cpp:2502
GalleryThumbView::RotateCW
void RotateCW()
Definition: gallerythumbview.h:73
GalleryThumbView::RepeatOn
static void RepeatOn()
Definition: gallerythumbview.h:123
UIUtilDisp::Assign
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
GalleryThumbView::m_scanProgressText
MythUIText * m_scanProgressText
Definition: gallerythumbview.h:137
GalleryThumbView::MarkInvertAll
void MarkInvertAll()
Invert all marked items.
Definition: gallerythumbview.cpp:1528
DirectoryView::LoadFromDb
bool LoadFromDb(int parentId) override
Populate view from database as images/subdirs of a directory. View is ordered: Parent dir,...
Definition: galleryviews.cpp:498
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:906
GalleryThumbView::RecursiveSlideshow
void RecursiveSlideshow()
Definition: gallerythumbview.h:71
InfoList::Hide
bool Hide()
Remove infolist from display.
Definition: galleryinfo.cpp:119
TransferThread::ImageSet
QSet< ImagePtrK > ImageSet
Definition: gallerythumbview.cpp:68
GalleryThumbView::m_emptyText
MythUIText * m_emptyText
Definition: gallerythumbview.h:133
GalleryThumbView::Move
void Move()
Move marked images to selected dir. If no marked files, use previously marked files....
Definition: gallerythumbview.cpp:2031
kImageFile
@ kImageFile
A picture.
Definition: imagetypes.h:39
MythMenu
Definition: mythdialogbox.h:99
ImageManagerFe::MoveDbImages
QString MoveDbImages(const ImagePtrK &destDir, ImageListK &images, const QString &srcPath)
Moves database images (but not the files themselves).
Definition: imagemanager.cpp:2268
GalleryThumbView::m_mgr
ImageManagerFe & m_mgr
Manages the images.
Definition: gallerythumbview.h:145
kUserCaption
@ kUserCaption
Exif comments.
Definition: gallerythumbview.h:18
GalleryThumbView::SetCover
void SetCover()
Definition: gallerythumbview.h:101
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:404
GallerySlideView::ImageSelected
void ImageSelected(int)
ImagePtr
QSharedPointer< ImageItem > ImagePtr
Definition: imagetypes.h:159
GalleryThumbView::m_editsAllowed
bool m_editsAllowed
Edit privileges.
Definition: gallerythumbview.h:162
MythConfirmationDialog
Dialog asking for user confirmation. Ok and optional Cancel button.
Definition: mythdialogbox.h:272
ImageDbReader::SetType
void SetType(int showType)
Definition: imagemanager.h:411
GalleryThumbView::HidePictures
void HidePictures()
Definition: gallerythumbview.h:105
ImageManagerFe
The image manager for use by Frontends.
Definition: imagemanager.h:459
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:687
MythUIButtonListItem::SetImage
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
Definition: mythuibuttonlist.cpp:3420
FlatView::GetParentId
int GetParentId() const
Definition: galleryviews.h:105
GalleryThumbView::Copy
void Copy()
Definition: gallerythumbview.h:119
std
Definition: mythchrono.h:23
WaitUntilDone
static void WaitUntilDone(MThread &worker)
Runs a worker thread and waits for it to finish.
Definition: gallerythumbview.cpp:146
DialogCompletionEvent
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
GalleryThumbView::DoMarkAll
void DoMarkAll(bool mark=true)
Mark or unmark all items.
Definition: gallerythumbview.cpp:1513
ImageIdList
QList< int > ImageIdList
Definition: imagetypes.h:60
ShellThread::GetResult
int GetResult(void) const
Definition: gallerythumbview.cpp:39
ImageManagerFe::CrumbName
QString CrumbName(ImageItemK &im, bool getPath=false) const
Return a displayable name (with optional path) for an image.
Definition: imagemanager.cpp:2384
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:132
MythUIType::SetVisible
virtual void SetVisible(bool visible)
Definition: mythuitype.cpp:1108
RemoteFile::MoveFile
static bool MoveFile(const QString &src, const QString &dst, bool overwrite=false)
Definition: remotefile.cpp:670
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
GalleryThumbView::DeleteItem
void DeleteItem()
Confirm user deletion of an item.
Definition: gallerythumbview.cpp:1615
DirectoryView::GetMenuSubjects
MenuSubjects GetMenuSubjects()
Determine current selection, markings & various info to support menu display.
Definition: galleryviews.cpp:807
GalleryThumbView::ShowHidden
void ShowHidden()
Definition: gallerythumbview.h:98
PHOTO_DB_ID
static constexpr int PHOTO_DB_ID
Definition: imagetypes.h:29
DialogCompletionEvent::kEventType
static const Type kEventType
Definition: mythdialogbox.h:57
GalleryThumbView::m_zoomWidgets
QList< MythUIButtonList * > m_zoomWidgets
Theme buttonlist widgets implementing zoom levels.
Definition: gallerythumbview.h:141
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
SlideOrderType
SlideOrderType
Order of images in slideshow.
Definition: galleryviews.h:21
MythUIButtonList::SetItemCurrent
void SetItemCurrent(MythUIButtonListItem *item)
Definition: mythuibuttonlist.cpp:1554
GalleryThumbView::UnhideMarked
void UnhideMarked()
Definition: gallerythumbview.h:95
GalleryThumbView::~GalleryThumbView
~GalleryThumbView() override
Destructor.
Definition: gallerythumbview.cpp:181
build_compdb.action
action
Definition: build_compdb.py:9
DirectoryView::ClearCache
void ClearCache()
Clears UI cache.
Definition: galleryviews.cpp:887
GalleryThumbView::m_zoomLevel
int m_zoomLevel
Definition: gallerythumbview.h:142
GalleryThumbView::RemoveImages
void RemoveImages(const QStringList &ids, bool deleted=true)
Cleanup UI & image caches when a device is removed.
Definition: gallerythumbview.cpp:575
GalleryThumbView::MarkAll
void MarkAll()
Definition: gallerythumbview.h:87
DirectoryView::Clear
void Clear(bool resetParent=true)
Resets view.
Definition: galleryviews.cpp:712
ShellThread::ShellThread
ShellThread(QString cmd, QString path)
Definition: gallerythumbview.cpp:36
GalleryThumbView::UnmarkItem
void UnmarkItem()
Definition: gallerythumbview.h:85
MThread::objectName
QString objectName(void) const
Definition: mthread.cpp:243
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:323
GalleryThumbView::DoShowType
void DoShowType(int type)
Show/hide pictures or videos.
Definition: gallerythumbview.cpp:1773
GalleryThumbView::Slideshow
void Slideshow()
Definition: gallerythumbview.h:70
DirectoryView::RemoveImage
QStringList RemoveImage(int id, bool deleted=false)
Clear file/dir and all its ancestors from UI cache so that ancestor thumbnails are recalculated....
Definition: galleryviews.cpp:902
GalleryThumbView::DoMarkItem
void DoMarkItem(bool mark)
Mark or unmark a single item.
Definition: gallerythumbview.cpp:1495
GalleryThumbView::MenuTransform
void MenuTransform(MythMenu *mainMenu)
Add a Transform submenu.
Definition: gallerythumbview.cpp:1152
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:838
ThumbPair
QPair< int, QString > ThumbPair
Definition: imagetypes.h:64
MenuSubjects::m_selected
ImagePtrK m_selected
Selected item.
Definition: galleryviews.h:66
kOrdered
@ kOrdered
Ordered as per user setting GallerySortOrder.
Definition: galleryviews.h:22
GalleryThumbView::ThumbLocation
QPair< MythUIButtonListItem *, int > ThumbLocation
Definition: gallerythumbview.h:157
ImageManagerFe::DeleteFiles
QString DeleteFiles(const ImageIdList &ids)
Delete images.
Definition: imagemanager.cpp:2295
GalleryThumbView::m_captionText
MythUIText * m_captionText
Definition: gallerythumbview.h:131
ShellThread::m_path
QString m_path
Definition: gallerythumbview.cpp:58
GalleryThumbView::DoScanAction
void DoScanAction(bool start)
Action scan request.
Definition: gallerythumbview.cpp:1408
GalleryThumbView::ResetExifMarked
void ResetExifMarked()
Definition: gallerythumbview.h:82
InfoList::Create
bool Create(bool focusable)
Initialise buttonlist from XML.
Definition: galleryinfo.cpp:67
galleryconfig.h
Provides Gallery configuration screens.
MythTextInputDialog
Dialog prompting the user to enter a text string.
Definition: mythdialogbox.h:314
MythCoreContext::SaveSetting
void SaveSetting(const QString &key, int newValue)
Definition: mythcorecontext.cpp:881
GalleryThumbView::HideVideos
void HideVideos()
Definition: gallerythumbview.h:106
GalleryThumbView::MarkItem
void MarkItem()
Definition: gallerythumbview.h:84
GALLERY_DB_ID
static constexpr int GALLERY_DB_ID
Definition: imagetypes.h:27
DirectoryView
A datastore of images for display by a screen. Provides an ordered list of dirs & images from a singl...
Definition: galleryviews.h:170
kDevice
@ kDevice
Storage Group and local mounted media.
Definition: imagetypes.h:36
GetMythUI
MythUIHelper * GetMythUI()
Definition: mythuihelper.cpp:66
MythUIButtonList
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
Definition: mythuibuttonlist.h:191
clean_comment
static QString clean_comment(const QString &comment)
Definition: gallerythumbview.cpp:25
DirectoryView::IsMarked
bool IsMarked(int id) const
Definition: galleryviews.h:188
MythUIProgressBar::SetVisible
void SetVisible(bool visible) override
Definition: mythuiprogressbar.cpp:196
DirectoryView::ClearMarked
void ClearMarked()
Unmark all items.
Definition: galleryviews.cpp:766
GalleryThumbView::MenuMarked
void MenuMarked(MythMenu *mainMenu)
Adds a Marking submenu.
Definition: gallerythumbview.cpp:1070
MythUIButtonListItem::setChecked
void setChecked(CheckState state)
Definition: mythuibuttonlist.cpp:3620
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:52
kNameCaption
@ kNameCaption
Filenames.
Definition: gallerythumbview.h:16
kRandom
@ kRandom
Random selection from view. An image may be absent or appear multiple times.
Definition: galleryviews.h:24
GalleryThumbView::Close
void Close() override
Exit Gallery.
Definition: gallerythumbview.cpp:191
ImageManagerFe::ScanQuery
static QStringList ScanQuery()
Returns storage group scanner status.
Definition: imagemanager.cpp:2039
ShowOkPopup
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
Definition: mythdialogbox.cpp:562
ImageManagerFe::RenameFile
QString RenameFile(const ImagePtrK &im, const QString &name)
Rename an image.
Definition: imagemanager.cpp:2204
ImageManagerFe::ShortDateOf
QString ShortDateOf(const ImagePtrK &im) const
Return a short datestamp for thumbnail captions.
Definition: imagemanager.cpp:2351
GalleryThumbView::Eject
void Eject()
Remove local device (or Import) from Gallery.
Definition: gallerythumbview.cpp:1886
MythObservable::removeListener
void removeListener(QObject *listener)
Remove a listener to the observable.
Definition: mythobservable.cpp:55
GalleryThumbView::RotateCWMarked
void RotateCWMarked()
Definition: gallerythumbview.h:78
ShellThread::run
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: gallerythumbview.cpp:41
MythCoreContext::SaveBoolSetting
void SaveBoolSetting(const QString &key, bool newValue)
Definition: mythcorecontext.h:160
TransferThread::m_dialog
MythUIProgressDialog * m_dialog
Images for which copy/move failed.
Definition: gallerythumbview.cpp:138
ShellThread::m_result
int m_result
Definition: gallerythumbview.cpp:56
kCloneDir
@ kCloneDir
A device sub dir comprised from multiple SG dirs.
Definition: imagetypes.h:37
kVideoOnly
@ kVideoOnly
Hide pictures.
Definition: imagemanager.h:80
GalleryThumbView::ShowType
void ShowType()
Definition: gallerythumbview.h:104
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:898
GalleryThumbView::StopScan
void StopScan()
Definition: gallerythumbview.h:112
GalleryThumbView::Start
void Start()
Start Thumbnail screen.
Definition: gallerythumbview.cpp:599
MenuSubjects::m_hiddenMarked
bool m_hiddenMarked
Is any marked item hidden ?
Definition: galleryviews.h:71
MythUIButtonListItem::NotChecked
@ NotChecked
Definition: mythuibuttonlist.h:46
ImageManagerFe::DetectLocalDevices
bool DetectLocalDevices()
Detect and scan local devices.
Definition: imagemanager.cpp:2418