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