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 #include <utility>
8 
9 #include "compat.h"
10 
11 #include "mythuitext.h"
12 #include "mythprogressdialog.h"
13 #include "mythuiprogressbar.h"
14 #include "remotefile.h"
15 #include "mythsystemlegacy.h"
16 #include "mythdialogbox.h"
17 
18 #include "galleryconfig.h"
19 
20 #define LOC QString("Thumbview: ")
21 
22 
24 class ShellThread: public MThread
25 {
26 public:
27  ShellThread(QString cmd, QString path)
28  : MThread("Import"), m_command(std::move(cmd)), m_path(std::move(path)) {}
29 
30  int GetResult(void) { return m_result; }
31 
32  void run() override // MThread
33  {
34  RunProlog();
35 
36  QString cmd = QString("cd %1 && %2").arg(m_path, m_command);
37  LOG(VB_GENERAL, LOG_INFO, QString("Executing \"%1\"").arg(cmd));
38 
39  m_result = myth_system(cmd);
40 
41  LOG(VB_GENERAL, LOG_INFO, QString(" ...with result %1").arg(m_result));
42 
43  RunEpilog();
44  }
45 
46 private:
47  int m_result {0};
48  QString m_command;
49  QString m_path;
50 };
51 
52 
54 class TransferThread : public MThread
55 {
56  Q_DECLARE_TR_FUNCTIONS(FileTransferWorker);
57 public:
58  typedef QMap<ImagePtrK, QString> TransferMap;
59  typedef QSet<ImagePtrK> ImageSet;
60 
61  TransferThread(const TransferMap &files, bool move, MythUIProgressDialog *dialog)
62  : MThread("FileTransfer"),
63  m_move(move), m_files(files), m_dialog(dialog) {}
64 
65  ImageSet GetResult(void) { return m_failed; }
66 
67  void run() override // MThread
68  {
69  RunProlog();
70 
71  QString action = m_move ? tr("Moving") : tr("Copying");
72 
73  // Sum file sizes
74  int total = 0;
75  foreach (ImagePtrK im, m_files.keys())
76  total += im->m_size;
77 
78  int progressSize = 0;
79  foreach (ImagePtrK im, m_files.keys())
80  {
81  // Update progress dialog
82  if (m_dialog)
83  {
84  QString message = QString("%1 %2\n%3")
85  .arg(action, QFileInfo(im->m_url).fileName(),
86  ImageAdapterBase::FormatSize(im->m_size / 1024));
87 
88  ProgressUpdateEvent *pue =
89  new ProgressUpdateEvent(progressSize, total, message);
90  QApplication::postEvent(m_dialog, pue);
91  }
92 
93  QString newPath = m_files.value(im);
94  LOG(VB_FILE, LOG_INFO, QString("%2 %3 -> %4")
95  .arg(action, im->m_url, newPath));
96 
97  bool success = m_move ? RemoteFile::MoveFile(im->m_url, newPath)
98  : RemoteFile::CopyFile(im->m_url, newPath,
99  false, true);
100  if (!success)
101  {
102  // Flag failures
103  m_failed.insert(im);
104 
105  LOG(VB_GENERAL, LOG_ERR,
106  QString("%1: Failed to copy/move %2 -> %3")
107  .arg(objectName(), im->m_url, m_files[im]));
108  }
109 
110  progressSize += im->m_size;
111  }
112 
113  // Update progress dialog
114  if (m_dialog)
115  {
116  ProgressUpdateEvent *pue =
117  new ProgressUpdateEvent(progressSize, total, tr("Complete"));
118  QApplication::postEvent(m_dialog, pue);
119  }
120 
121  RunEpilog();
122  }
123 
124 private:
125  bool m_move;
129 };
130 
131 
136 static void WaitUntilDone(MThread &worker)
137 {
138  worker.start();
139  while (!worker.isFinished())
140  {
141  std::this_thread::sleep_for(std::chrono::milliseconds(1));
142  qApp->processEvents();
143  }
144 }
145 
146 
153  : MythScreenType(parent, name),
154  m_popupStack(*GetMythMainWindow()->GetStack("popup stack")),
155  m_mgr(ImageManagerFe::getInstance()),
156  // This screen uses a single fixed view (Parent dir, ordered dirs, ordered images)
157  m_view(new DirectoryView(kOrdered)),
158  m_infoList(*this),
159  // Start in edit mode unless a password exists
160  m_editsAllowed(gCoreContext->GetSetting("GalleryPassword").isEmpty())
161 {
162  // Hide hidden when edits disallowed
163  if (!m_editsAllowed)
164  m_mgr.SetVisibility(false);
165 }
166 
167 
172 {
173  LOG(VB_GUI, LOG_DEBUG, LOC + "Exiting Gallery");
174  delete m_view;
175 }
176 
177 
182 {
183  LOG(VB_GUI, LOG_DEBUG, LOC + "Closing Gallery");
184 
186 
187  // Cleanup local devices
189 
190  // Cleanup view
191  m_view->Clear();
192 
194 }
195 
196 
201 {
202  if (!LoadWindowFromXML("image-ui.xml", "gallery", this))
203  return false;
204 
205  // Determine zoom levels supported by theme
206  // images0 must exist; images1, images2 etc. are optional and enable zoom
207  int zoom = 0;
208  MythUIButtonList *widget;
209  do
210  {
211  QString name = QString("images%1").arg(zoom++);
212  widget = dynamic_cast<MythUIButtonList *>(this->GetChild(name));
213  if (widget)
214  {
215  m_zoomWidgets.append(widget);
216  widget->SetVisible(false);
217  }
218  }
219  while (widget);
220 
221  if (m_zoomWidgets.isEmpty())
222  {
223  LOG(VB_GENERAL, LOG_ERR, LOC + "Screen 'Gallery' is missing 'images0'");
224  return false;
225  }
226  LOG(VB_GUI, LOG_DEBUG, LOC + QString("Screen 'Gallery' found %1 zoom levels")
227  .arg(m_zoomWidgets.size()));
228 
229  // File details list is managed elsewhere
230  if (!m_infoList.Create(false))
231  {
232  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot load 'Info buttonlist'");
233  return false;
234  }
235 
236  UIUtilW::Assign(this, m_captionText, "caption");
237  UIUtilW::Assign(this, m_emptyText, "noimages");
238  UIUtilW::Assign(this, m_positionText, "position");
239  UIUtilW::Assign(this, m_crumbsText, "breadcrumbs");
240  UIUtilW::Assign(this, m_hideFilterText, "hidefilter");
241  UIUtilW::Assign(this, m_typeFilterText, "typefilter");
242  UIUtilW::Assign(this, m_scanProgressText, "scanprogresstext");
243  UIUtilW::Assign(this, m_scanProgressBar, "scanprogressbar");
244 
245  if (m_scanProgressText)
247  if (m_scanProgressBar)
249 
250  BuildFocusList();
251 
252  // Initialise list widget with appropriate zoom level for this theme.
253  m_zoomLevel = gCoreContext->GetNumSetting("GalleryZoomLevel", 0);
254  SelectZoomWidget(0);
255 
256  return true;
257 }
258 
259 
264 bool GalleryThumbView::keyPressEvent(QKeyEvent *event)
265 {
266  if (GetFocusWidget()->keyPressEvent(event))
267  return true;
268 
269  QStringList actions;
270  bool handled = GetMythMainWindow()->TranslateKeyPress("Images", event, actions);
271 
272  for (int i = 0; i < actions.size() && !handled; i++)
273  {
274  QString action = actions[i];
275  handled = true;
276 
277  if (action == "MENU")
278  MenuMain();
279  else if (action == "INFO")
280  ShowDetails();
281  else if (action == "ZOOMIN")
282  ZoomIn();
283  else if (action == "ZOOMOUT")
284  ZoomOut();
285  else if (action == "ROTRIGHT")
286  RotateCW();
287  else if (action == "ROTLEFT")
288  RotateCCW();
289  else if (action == "FLIPHORIZONTAL")
290  FlipHorizontal();
291  else if (action == "FLIPVERTICAL")
292  FlipVertical();
293  else if (action == "COVER")
294  {
295  ImagePtrK im = m_view->GetSelected();
296  if (m_editsAllowed && im)
297  {
298  if (im == m_view->GetParent())
299  // Reset dir
300  m_mgr.SetCover(im->m_id, 0);
301  else
302  // Set parent cover
303  m_mgr.SetCover(im->m_parentId, im->m_id);
304  }
305  }
306  else if (action == "PLAY")
307  Slideshow();
308  else if (action == "RECURSIVESHOW")
309  {
310  ImagePtrK im = m_view->GetSelected();
311  if (im && im->IsDirectory())
313  }
314  else if (action == "MARK")
315  {
316  ImagePtrK im = m_view->GetSelected();
317  if (m_editsAllowed && im && im != m_view->GetParent())
318  MarkItem(!m_view->IsMarked(im->m_id));
319  }
320  else if (action == "ESCAPE" && !GetMythMainWindow()->IsExitingToMain())
321  {
322  // Exit info list, if shown
323  handled = m_infoList.Hide();
324 
325  // Ascend the tree unless parent is root,
326  // or a device and multiple devices/imports exist
327  if (!handled)
328  {
329  ImagePtrK node = m_view->GetParent();
330  if (node && node->m_id != GALLERY_DB_ID
331  && (!node->IsDevice() || m_mgr.DeviceCount() > 0))
332  handled = DirSelectUp();
333  }
334  }
335  else
336  handled = false;
337  }
338 
339  if (!handled)
340  handled = MythScreenType::keyPressEvent(event);
341 
342  return handled;
343 }
344 
345 
350 void GalleryThumbView::customEvent(QEvent *event)
351 {
352 
353  if (event->type() == MythEvent::MythEventMessage)
354  {
355  MythEvent *me = static_cast<MythEvent *>(event);
356  const QString& mesg = me->Message();
357  QStringList extra = me->ExtraDataList();
358 
359  // Internal messages contain a hostname. Ignore other FE messages
360  QStringList token = mesg.split(' ');
361  if (token.size() >= 2 && token[1] != gCoreContext->GetHostName())
362  return;
363 
364  if (token[0] == "IMAGE_METADATA")
365  {
366  int id = extra[0].toInt();
367  ImagePtrK selected = m_view->GetSelected();
368 
369  if (selected && selected->m_id == id)
370  m_infoList.Display(*selected, extra.mid(1));
371  }
372  else if (token[0] == "THUMB_AVAILABLE")
373  {
374  int id = extra[0].toInt();
375 
376  // Note existance of all thumbs
377  m_thumbExists.insert(id);
378 
379  // Get all buttons waiting for this thumbnail
380  QList<ThumbLocation> affected = m_pendingMap.values(id);
381 
382  // Only concerned with thumbnails we've requested
383  if (affected.isEmpty())
384  return;
385 
386  LOG(VB_GENERAL, LOG_DEBUG, LOC +
387  QString("Rx %1 : %2").arg(token[0], extra.join(",")));
388 
389  // Thumb url was cached when request was sent
390  QString url = m_view->GetCachedThumbUrl(id);
391 
392  // Set thumbnail for each button now it exists
393  foreach(const ThumbLocation &location, affected)
394  {
395  MythUIButtonListItem *button = location.first;
396  int index = location.second;
397 
398  ImagePtrK im = button->GetData().value<ImagePtrK>();
399  if (im)
400  UpdateThumbnail(button, im, url, index);
401  }
402 
403  // Cancel pending request
404  m_pendingMap.remove(id);
405  }
406  else if (token[0] == "IMAGE_DB_CHANGED")
407  {
408  // Expects csv list of deleted ids, csv list of changed ids
409  LOG(VB_GENERAL, LOG_DEBUG, LOC +
410  QString("Rx %1 : %2").arg(token[0], extra.join(",")));
411 
412  if (!extra.isEmpty())
413  {
414  QStringList idDeleted =
415  extra[0].split(",", QString::SkipEmptyParts);
416 
417  RemoveImages(idDeleted);
418  }
419  if (extra.size() >= 2)
420  {
421  QStringList idChanged =
422  extra[1].split(",", QString::SkipEmptyParts);
423  RemoveImages(idChanged, false);
424  }
425 
426  // Refresh display
428  }
429  else if (token[0] == "IMAGE_DEVICE_CHANGED")
430  {
431  // Expects list of url prefixes
432  LOG(VB_GENERAL, LOG_DEBUG, LOC +
433  QString("Rx %1 : %2").arg(token[0], extra.join(",")));
434 
435  // Clear everything. Local devices will be rebuilt
436  m_view->Clear();
437  m_thumbExists.clear();
438 
439  // Remove thumbs & images from image cache using supplied prefixes
440  foreach(const QString &url, extra)
442 
443  // Refresh display
445  }
446  else if (token[0] == "IMAGE_SCAN_STATUS" && extra.size() == 3)
447  {
448  // Expects scanner id, scanned#, total#
449  UpdateScanProgress(extra[0], extra[1].toInt(), extra[2].toInt());
450  }
451  }
452  else if (event->type() == DialogCompletionEvent::kEventType)
453  {
455 
456  QString resultid = dce->GetId();
457  int buttonnum = dce->GetResult();
458 
459  if (resultid == "FileRename")
460  {
461  QString newName = dce->GetResultText();
463  {
464  QString err = m_mgr.RenameFile(m_menuState.m_selected,
465  newName);
466  if (!err.isEmpty())
467  ShowOkPopup(err);
468  }
469  }
470  else if (resultid == "MakeDir")
471  {
473  {
474  // Prohibit subtrees
475  QString name = dce->GetResultText();
476  QString err = name.contains("/")
477  ? tr("Invalid Name")
479  QStringList(name));
480  if (!err.isEmpty())
481  ShowOkPopup(err);
482  }
483  }
484  else if (resultid == "SlideOrderMenu")
485  {
486  SlideOrderType slideOrder = kOrdered;
487 
488  switch (buttonnum)
489  {
490  case 0: slideOrder = kOrdered; break;
491  case 1: slideOrder = kShuffle; break;
492  case 2: slideOrder = kRandom; break;
493  case 3: slideOrder = kSeasonal; break;
494  }
495  gCoreContext->SaveSetting("GallerySlideOrder", slideOrder);
496  LOG(VB_FILE, LOG_DEBUG, LOC + QString("Order %1").arg(slideOrder));
497  }
498  else if (resultid == "ImageCaptionMenu")
499  {
500  ImageCaptionType captions = kNoCaption;
501 
502  switch (buttonnum)
503  {
504  case 0: captions = kNameCaption; break;
505  case 1: captions = kDateCaption; break;
506  case 2: captions = kUserCaption; break;
507  case 3: captions = kNoCaption; break;
508  }
509  gCoreContext->SaveSetting("GalleryImageCaption", captions);
510  BuildImageList();
511  }
512  else if (resultid == "DirCaptionMenu")
513  {
514  ImageCaptionType captions = kNoCaption;
515 
516  switch (buttonnum)
517  {
518  case 0: captions = kNameCaption; break;
519  case 1: captions = kDateCaption; break;
520  case 2: captions = kNoCaption; break;
521  }
522  gCoreContext->SaveSetting("GalleryDirCaption", captions);
523  BuildImageList();
524  }
525  else if (resultid == "Password")
526  {
527  QString password = dce->GetResultText();
528  m_editsAllowed = (password == gCoreContext->GetSetting("GalleryPassword"));
529  }
530  else if (buttonnum == 1)
531  {
532  // Confirm current file deletion
533  QString err;
534  if (resultid == "ConfirmDelete" && m_menuState.m_selected)
535  {
537  err = m_mgr.DeleteFiles(ids);
538  }
539  // Confirm marked file deletion
540  else if (resultid == "ConfirmDeleteMarked")
541  {
543  }
544  else
545  return;
546 
547  if (!err.isEmpty())
548  ShowOkPopup(err);
549  }
550  }
551 }
552 
553 
559 void GalleryThumbView::RemoveImages(const QStringList &ids, bool deleted)
560 {
561  foreach (const QString &id, ids)
562  {
563  // Remove image from view
564  QStringList urls = m_view->RemoveImage(id.toInt(), deleted);
565  // Cleanup url lookup
566  m_thumbExists.remove(id.toInt());
567 
568  // Remove thumbs & images from image cache
569  foreach(const QString &url, urls)
570  {
571  LOG(VB_FILE, LOG_DEBUG, LOC +
572  QString("Clearing image cache of '%1'").arg(url));
573 
575  }
576  }
577 }
578 
579 
584 {
585  // Detect any running BE scans
586  // Expects OK, scanner id, current#, total#
587  QStringList message = m_mgr.ScanQuery();
588  if (message.size() == 4 && message[0] == "OK")
589  {
590  UpdateScanProgress(message[1], message[2].toInt(), message[3].toInt());
591  }
592 
593  // Only receive events after device/scan status has been established
594  gCoreContext->addListener(this);
595 
596  // Start at Root if devices exist. Otherwise go straight to SG node
598 
599  LoadData(start);
600 }
601 
602 
608 {
610 
611  // Load view for parent directory
612  if (m_view->LoadFromDb(parent))
613  {
614  m_imageList->SetVisible(true);
615  if (m_emptyText)
616  {
617  m_emptyText->SetVisible(false);
618  m_emptyText->Reset();
619  }
620 
621  // Construct the buttonlist
622  BuildImageList();
623  }
624  else
625  {
626  m_infoList.Hide();
627  m_imageList->SetVisible(false);
628  if (m_emptyText)
629  {
630  m_emptyText->SetVisible(true);
631  m_emptyText->SetText(tr("No images found.\n"
632  "Scan storage group using menu,\n"
633  "or insert/mount local media.\n"));
634  }
635  }
636 }
637 
638 
643 {
644  m_imageList->Reset();
645  m_pendingMap.clear();
646 
647  // Get parent & all children
648  ImageListK nodes = m_view->GetAllNodes();
649  ImagePtrK selected = m_view->GetSelected();
650 
651  // go through the entire list and update
652  foreach(const ImagePtrK &im, nodes)
653  if (im)
654  {
655  // Data must be set by constructor: First item is automatically
656  // selected and must have data available for selection event, as
657  // subsequent reselection of same item will always fail.
658  MythUIButtonListItem *item =
659  new MythUIButtonListItem(m_imageList, "", qVariantFromValue(im));
660 
661  item->setCheckable(true);
663 
664  // assign and display all information about
665  // the current item, like title and subdirectory count
666  UpdateImageItem(item);
667 
668  // Treat parent differently
669  if (im == nodes[0])
670  {
671  // Only non-root parents can ascend
672  if (im->m_id != GALLERY_DB_ID)
673  item->DisplayState("upfolder", "parenttype");
674  }
675  else if (im == selected)
676  // Reinstate the active button item. Note this would fail for parent
678  }
679 }
680 
681 
687 {
688  ImagePtrK im = item->GetData().value<ImagePtrK >();
689  if (!im)
690  return;
691 
692  // Allow themes to distinguish between roots, folders, pics, videos
693  switch (im->m_type)
694  {
695  case kDevice:
696  case kCloneDir:
697  case kDirectory:
698  if (im->m_dirCount > 0)
699  item->SetText(QString("%1/%2")
700  .arg(im->m_fileCount).arg(im->m_dirCount),
701  "childcount");
702  else
703  item->SetText(QString::number(im->m_fileCount), "childcount");
704 
705  item->DisplayState(im->IsDevice() ? "device" : "subfolder", "buttontype");
706  break;
707 
708  case kImageFile:
709  item->DisplayState("image", "buttontype");
710  break;
711 
712  case kVideoFile:
713  item->DisplayState("video", "buttontype");
714  break;
715 
716  default:
717  break;
718  }
719 
720  // Allow theme to distinguish visible/hidden nodes
721  QString hideState = (im->m_isHidden) ? "hidden" : "visible";
722  item->DisplayState(hideState, "buttonstate");
723 
724  // Caption
725  QString text;
727  im->IsFile() ? "GalleryImageCaption"
728  : "GalleryDirCaption");
729  switch (show)
730  {
731  case kNameCaption: text = m_mgr.CrumbName(*im); break;
732  case kDateCaption: text = m_mgr.ShortDateOf(im); break;
733  case kUserCaption: text = im->m_comment; break;
734  default:
735  case kNoCaption: text = ""; break;
736  }
737  item->SetText(text);
738 
739  // Set marked state
741  = m_view->IsMarked(im->m_id)
744 
745  item->setChecked(state);
746 
747  // Thumbnails required
748  ImageIdList request;
749 
750  if (im->m_thumbNails.size() == 1)
751  {
752  // Single thumbnail
753  QString url = CheckThumbnail(item, im, request, 0);
754 
755  if (!url.isEmpty())
756  UpdateThumbnail(item, im, url, 0);
757  }
758  else
759  {
760  // Dir showing up to 4 thumbs. Set them all at same time
761  InfoMap thumbMap;
762  for (int index = 0; index < im->m_thumbNails.size(); ++index)
763  {
764  QString url = CheckThumbnail(item, im, request, index);
765  if (!url.isEmpty())
766  thumbMap.insert(QString("thumbimage%1").arg(index), url);
767  }
768  if (!thumbMap.isEmpty())
769  item->SetImageFromMap(thumbMap);
770  }
771 
772  // Request creation/verification of unknown thumbnails.
773  if (!request.isEmpty())
774  m_mgr.CreateThumbnails(request, im->IsDirectory());
775 }
776 
777 
790  ImageIdList &request, int index)
791 {
792  ThumbPair thumb(im->m_thumbNails.at(index));
793  int id = thumb.first;
794 
795  if (m_thumbExists.contains(id))
796  return thumb.second;
797 
798  // Request BE thumbnail check if it is not already pending
799  if (!m_pendingMap.contains(id))
800  request << id;
801 
802  // Note this button is awaiting an update
803  m_pendingMap.insertMulti(id, qMakePair(item, index));
804 
805  return "";
806 }
807 
808 
817  const ImagePtrK& im, const QString &url,
818  int index)
819 {
820  if (im->m_thumbNails.size() == 1)
821  {
822  // Pics, dirs & videos use separate widgets
823  switch (im->m_type)
824  {
825  case kImageFile: button->SetImage(url); break;
826  case kVideoFile: button->SetImage(url, "videoimage"); break;
827  default: button->SetImage(url, "folderimage"); break;
828  }
829  }
830  else
831  // Dir with 4 thumbnails
832  button->SetImage(url, QString("thumbimage%1").arg(index));
833 }
834 
835 
843 void GalleryThumbView::UpdateScanProgress(const QString &scanner,
844  int current, int total)
845 {
846  // Scan update
847  m_scanProgress.insert(scanner, qMakePair(current, total));
848 
849  // Detect end of this scan
850  if (current >= total)
851  {
852  LOG(VB_GUI, LOG_DEBUG, LOC + QString("Scan Finished %1 %2/%3")
853  .arg(scanner).arg(current).arg(total));
854 
855  // Mark inactive scanner
856  m_scanActive.remove(scanner);
857 
858  // Detect end of last scan
859  if (m_scanActive.isEmpty())
860  {
861  if (m_scanProgressText)
862  {
865  }
866  if (m_scanProgressBar)
867  {
870  }
871 
872  m_scanProgress.clear();
873 
874  return;
875  }
876  }
877  else
878  {
879  // Detect first scan update
880  if (m_scanActive.isEmpty())
881  {
882  // Show progressbar when first scan starts
883  if (m_scanProgressBar)
884  {
887  }
888  if (m_scanProgressText)
890  }
891 
892  if (!m_scanActive.contains(scanner))
893  {
894  LOG(VB_GUI, LOG_DEBUG, LOC + QString("Scan Started %1 %2/%3")
895  .arg(scanner).arg(current).arg(total));
896 
897  // Mark active scanner
898  m_scanActive.insert(scanner);
899  }
900  }
901 
902  // Aggregate all running scans
903  int currentAgg = 0, totalAgg = 0;
904  foreach (IntPair scan, m_scanProgress.values())
905  {
906  currentAgg += scan.first;
907  totalAgg += scan.second;
908  }
909 
910  if (m_scanProgressBar)
911  {
912  m_scanProgressBar->SetUsed(currentAgg);
913  m_scanProgressBar->SetTotal(totalAgg);
914  }
915  if (m_scanProgressText)
916  m_scanProgressText->SetText(tr("%L1 of %L3").arg(currentAgg).arg(totalAgg));
917 }
918 
919 
924 {
925  if (m_positionText)
927 
928  if (m_captionText)
929  m_captionText->Reset();
930 
931  if (m_crumbsText)
932  m_crumbsText->Reset();
933 
934  if (m_hideFilterText)
936 
937  if (m_typeFilterText)
939 }
940 
941 
947 {
948  ImagePtrK im = item->GetData().value<ImagePtrK >();
949  if (im)
950  {
951  // update the position in the node list
952  m_view->Select(im->m_id);
953 
954  // show the name/path of the image
955  if (m_crumbsText)
956  m_crumbsText->SetText(m_mgr.CrumbName(*im, true));
957 
958  if (m_captionText)
959  {
960  // show the date & comment of non-root nodes
961  QStringList text;
962  if (im->m_id != GALLERY_DB_ID)
963  {
964  if (im->IsFile() || im->IsDevice())
965  text << m_mgr.LongDateOf(im);
966 
967  if (!im->m_comment.isEmpty())
968  text << im->m_comment;
969  }
970  m_captionText->SetText(text.join(" - "));
971  }
972 
973  if (m_hideFilterText)
974  {
975  m_hideFilterText->SetText(m_mgr.GetVisibility() ? tr("Hidden") : "");
976  }
977 
978  if (m_typeFilterText)
979  {
980  QString text = "";
981  switch (m_mgr.GetType())
982  {
983  case kPicAndVideo : text = ""; break;
984  case kPicOnly : text = tr("Pictures"); break;
985  case kVideoOnly : text = tr("Videos"); break;
986  }
987  m_typeFilterText->SetText(text);
988  }
989 
990  // show the position of the image
991  if (m_positionText)
993 
994  // Update any file details information
995  m_infoList.Update(im);
996  }
997 }
998 
999 
1004 {
1005  // Create the main menu
1006  MythMenu *menu = new MythMenu(tr("Gallery Options"), this, "mainmenu");
1007 
1008  // Menu options depend on the marked files and the current node
1010 
1011  if (m_menuState.m_selected)
1012  {
1013  if (m_editsAllowed)
1014  {
1015  MenuMarked(menu);
1016  MenuPaste(menu);
1018  MenuAction(menu);
1019  }
1021  MenuShow(menu);
1022  if (!m_editsAllowed)
1023  menu->AddItem(tr("Enable Edits"), SLOT(ShowPassword()));
1024  }
1025 
1026  // Depends on current status of backend scanner - string(number(isBackend()))
1027  if (m_scanActive.contains("1"))
1028  menu->AddItem(tr("Stop Scan"), SLOT(StopScan()));
1029  else
1030  menu->AddItem(tr("Scan Storage Group"), SLOT(StartScan()));
1031 
1032  menu->AddItem(tr("Settings"), SLOT(ShowSettings()));
1033 
1034  MythDialogBox *popup = new MythDialogBox(menu, &m_popupStack, "menuPopup");
1035  if (popup->Create())
1036  m_popupStack.AddScreen(popup);
1037  else
1038  delete popup;
1039 }
1040 
1041 
1047 {
1048  ImagePtrK parent = m_view->GetParent();
1049 
1050  if (m_menuState.m_childCount == 0 || parent.isNull())
1051  return;
1052 
1053  QString title = tr("%L1 marked").arg(m_menuState.m_markedId.size());
1054  MythMenu *menu = new MythMenu(title, this, "markmenu");
1055 
1056  // Mark/unmark selected
1057  if (m_menuState.m_selected->IsFile())
1058  {
1060  menu->AddItem(tr("Unmark File"), SLOT(UnmarkItem()));
1061  else
1062  menu->AddItem(tr("Mark File"), SLOT(MarkItem()));
1063  }
1064  // Cannot mark/unmark parent dir from this level
1065  else if (!m_menuState.m_selected->IsDevice()
1066  && m_menuState.m_selected != parent)
1067  {
1069  menu->AddItem(tr("Unmark Directory"), SLOT(UnmarkItem()));
1070  else
1071  menu->AddItem(tr("Mark Directory"), SLOT(MarkItem()));
1072  }
1073 
1074  if (parent->m_id != GALLERY_DB_ID)
1075  {
1076  // Mark All if unmarked files exist
1078  menu->AddItem(tr("Mark All"), SLOT(MarkAll()));
1079 
1080  // Unmark All if marked files exist
1081  if (!m_menuState.m_markedId.isEmpty())
1082  {
1083  menu->AddItem(tr("Unmark All"), SLOT(UnmarkAll()));
1084  menu->AddItem(tr("Invert Marked"), SLOT(MarkInvertAll()));
1085  }
1086  }
1087 
1088  if (menu->IsEmpty())
1089  delete menu;
1090  else
1091  mainMenu->AddItem(tr("Mark"), nullptr, menu);
1092 }
1093 
1094 
1100 {
1101  // Can only copy/move into non-root dirs
1102  if (m_menuState.m_selected->IsDirectory()
1103  && m_menuState.m_selected->m_id != GALLERY_DB_ID)
1104  {
1105  // Operate on current marked files, if any
1107  if (files.isEmpty())
1108  files = m_menuState.m_prevMarkedId;
1109  if (files.isEmpty())
1110  return;
1111 
1112  QString title = tr("%L1 marked").arg(files.size());
1113 
1114  MythMenu *menu = new MythMenu(title, this, "pastemenu");
1115 
1116  menu->AddItem(tr("Move Marked Into"), SLOT(Move()));
1117  menu->AddItem(tr("Copy Marked Into"), SLOT(Copy()));
1118 
1119  mainMenu->AddItem(tr("Paste"), nullptr, menu);
1120  }
1121 }
1122 
1123 
1129 {
1130  // Operate on marked files, if any, otherwise selected node
1131  if (!m_menuState.m_markedId.isEmpty())
1132  {
1133  QString title = tr("%L1 marked").arg(m_menuState.m_markedId.size());
1134 
1135  MythMenu *menu = new MythMenu(title, this, "");
1136 
1137  menu->AddItem(tr("Rotate Marked CW"), SLOT(RotateCWMarked()));
1138  menu->AddItem(tr("Rotate Marked CCW"), SLOT(RotateCCWMarked()));
1139  menu->AddItem(tr("Flip Marked Horizontal"), SLOT(FlipHorizontalMarked()));
1140  menu->AddItem(tr("Flip Marked Vertical"), SLOT(FlipVerticalMarked()));
1141  menu->AddItem(tr("Reset Marked to Exif"), SLOT(ResetExifMarked()));
1142 
1143  mainMenu->AddItem(tr("Transforms"), nullptr, menu);
1144  }
1145  else if (m_menuState.m_selected->IsFile())
1146  {
1147  MythMenu *menu = new MythMenu(m_menuState.m_selected->m_baseName, this, "");
1148 
1149  menu->AddItem(tr("Rotate CW"), SLOT(RotateCW()));
1150  menu->AddItem(tr("Rotate CCW"), SLOT(RotateCCW()));
1151  menu->AddItem(tr("Flip Horizontal"), SLOT(FlipHorizontal()));
1152  menu->AddItem(tr("Flip Vertical"), SLOT(FlipVertical()));
1153  menu->AddItem(tr("Reset to Exif"), SLOT(ResetExif()));
1154 
1155  mainMenu->AddItem(tr("Transforms"), nullptr, menu);
1156  }
1157 }
1158 
1159 
1165 {
1166  MythMenu *menu;
1167  ImagePtrK selected = m_menuState.m_selected;
1168 
1169  // Operate on current marked files, if any
1170  if (!m_menuState.m_markedId.empty())
1171  {
1172  QString title = tr("%L1 marked").arg(m_menuState.m_markedId.size());
1173 
1174  menu = new MythMenu(title, this, "actionmenu");
1175 
1176  // Only offer Hide/Unhide if relevant
1178  menu->AddItem(tr("Hide Marked"), SLOT(HideMarked()));
1180  menu->AddItem(tr("Unhide Marked"), SLOT(UnhideMarked()));
1181 
1182  menu->AddItem(tr("Delete Marked"), SLOT(DeleteMarked()));
1183  }
1184  else
1185  {
1186  // Operate on selected file/dir
1187  menu = new MythMenu(selected->m_baseName, this, "actionmenu");
1188 
1189  // Prohibit actions on devices and parent dirs
1190  if (!selected->IsDevice() && selected != m_view->GetParent())
1191  {
1192  if (selected->m_isHidden)
1193  menu->AddItem(tr("Unhide"), SLOT(Unhide()));
1194  else
1195  menu->AddItem(tr("Hide"), SLOT(HideItem()));
1196 
1197  menu->AddItem(tr("Use as Cover"), SLOT(SetCover()));
1198  menu->AddItem(tr("Delete"), SLOT(DeleteItem()));
1199  menu->AddItem(tr("Rename"), SLOT(ShowRenameInput()));
1200  }
1201  else if (selected->m_userThumbnail)
1202  menu->AddItem(tr("Reset Cover"), SLOT(ResetCover()));
1203  }
1204 
1205  // Can only mkdir in a non-root dir
1206  if (selected->IsDirectory()
1207  && selected->m_id != GALLERY_DB_ID)
1208  menu->AddItem(tr("Create Directory"), SLOT(MakeDir()));
1209 
1210  // Only show import command on root, when defined
1211  if (selected->m_id == GALLERY_DB_ID
1212  && !gCoreContext->GetSetting("GalleryImportCmd").isEmpty())
1213  menu->AddItem(tr("Import"), SLOT(Import()));
1214 
1215  // Only show eject when devices (excluding import) exist
1216  if (selected->IsDevice() && selected->IsLocal())
1217  menu->AddItem(tr("Eject media"), SLOT(Eject()));
1218 
1219  if (menu->IsEmpty())
1220  delete menu;
1221  else
1222  mainMenu->AddItem(tr("Actions"), nullptr, menu);
1223 }
1224 
1225 
1231 {
1232  int order = gCoreContext->GetNumSetting("GallerySlideOrder", kOrdered);
1233 
1234  QString ordering;
1235  switch (order)
1236  {
1237  case kShuffle : ordering = tr("Shuffled"); break;
1238  case kRandom : ordering = tr("Random"); break;
1239  case kSeasonal : ordering = tr("Seasonal"); break;
1240  default:
1241  case kOrdered : ordering = tr("Ordered"); break;
1242  }
1243 
1244  MythMenu *menu = new MythMenu(tr("Slideshow") + " (" + ordering + ")",
1245  this, "SlideshowMenu");
1246 
1247  // Use selected dir or parent, if image selected
1248  if (m_menuState.m_selected->IsDirectory())
1249  {
1250  if (m_menuState.m_selected->m_fileCount > 0)
1251  menu->AddItem(tr("Directory"), SLOT(Slideshow()));
1252 
1253  if (m_menuState.m_selected->m_dirCount > 0)
1254  menu->AddItem(tr("Recursive"), SLOT(RecursiveSlideshow()));
1255  }
1256  else
1257  menu->AddItem(tr("Current Directory"), SLOT(Slideshow()));
1258 
1259  MythMenu *orderMenu = new MythMenu(tr("Slideshow Order"), this,
1260  "SlideOrderMenu");
1261 
1262  orderMenu->AddItem(tr("Ordered"), nullptr, nullptr, order == kOrdered);
1263  orderMenu->AddItem(tr("Shuffled"), nullptr, nullptr, order == kShuffle);
1264  orderMenu->AddItem(tr("Random"), nullptr, nullptr, order == kRandom);
1265  orderMenu->AddItem(tr("Seasonal"), nullptr, nullptr, order == kSeasonal);
1266 
1267  menu->AddItem(tr("Change Order"), nullptr, orderMenu);
1268 
1269  if (gCoreContext->GetBoolSetting("GalleryRepeat", false))
1270  menu->AddItem(tr("Turn Repeat Off"), SLOT(RepeatOff()));
1271  else
1272  menu->AddItem(tr("Turn Repeat On"), SLOT(RepeatOn()));
1273 
1274  mainMenu->AddItem(tr("Slideshow"), nullptr, menu);
1275 }
1276 
1277 
1283 {
1284  MythMenu *menu = new MythMenu(tr("Show Options"), this, "showmenu");
1285 
1286  int type = m_mgr.GetType();
1287  if (type == kPicAndVideo)
1288  {
1289  menu->AddItem(tr("Hide Pictures"), SLOT(HidePictures()));
1290  menu->AddItem(tr("Hide Videos"), SLOT(HideVideos()));
1291  }
1292  else
1293  menu->AddItem(type == kPicOnly ? tr("Show Videos") : tr("Show Pictures"),
1294  SLOT(ShowType()));
1295 
1296  int show = gCoreContext->GetNumSetting("GalleryImageCaption");
1297  MythMenu *captionMenu = new MythMenu(tr("Image Captions"), this,
1298  "ImageCaptionMenu");
1299 
1300  captionMenu->AddItem(tr("Name"), nullptr, nullptr, show == kNameCaption);
1301  captionMenu->AddItem(tr("Date"), nullptr, nullptr, show == kDateCaption);
1302  captionMenu->AddItem(tr("Comment"), nullptr, nullptr, show == kUserCaption);
1303  captionMenu->AddItem(tr("None"), nullptr, nullptr, show == kNoCaption);
1304 
1305  menu->AddItem(tr("Image Captions"), nullptr, captionMenu);
1306 
1307  show = gCoreContext->GetNumSetting("GalleryDirCaption");
1308  captionMenu = new MythMenu(tr("Directory Captions"), this, "DirCaptionMenu");
1309 
1310  captionMenu->AddItem(tr("Name"), nullptr, nullptr, show == kNameCaption);
1311  captionMenu->AddItem(tr("Date"), nullptr, nullptr, show == kDateCaption);
1312  captionMenu->AddItem(tr("None"), nullptr, nullptr, show == kNoCaption);
1313 
1314  menu->AddItem(tr("Directory Captions"), nullptr, captionMenu);
1315 
1316  if (m_editsAllowed)
1317  {
1318  if (m_mgr.GetVisibility())
1319  menu->AddItem(tr("Hide Hidden Items"), SLOT(HideHidden()));
1320  else
1321  menu->AddItem(tr("Show Hidden Items"), SLOT(ShowHidden()));
1322  }
1323 
1324  if (m_zoomLevel > 0)
1325  menu->AddItem(tr("Zoom Out"), SLOT(ZoomOut()));
1326  if (m_zoomLevel < m_zoomWidgets.size() - 1)
1327  menu->AddItem(tr("Zoom In"), SLOT(ZoomIn()));
1328 
1329  QString details = m_infoList.GetState() == kNoInfo
1330  ? tr("Show Details") : tr("Hide Details");
1331 
1332  menu->AddItem(details, SLOT(ShowDetails()));
1333 
1334  mainMenu->AddItem(tr("Show"), nullptr, menu);
1335 }
1336 
1337 
1343 {
1344  // Only update selection if image is currently displayed
1345  if (m_view->Select(id, -1))
1346  BuildImageList();
1347 }
1348 
1349 
1355 {
1356  if (!item)
1357  return;
1358 
1359  ImagePtrK im = item->GetData().value<ImagePtrK>();
1360  if (!im)
1361  return;
1362 
1363  switch (im->m_type)
1364  {
1365  case kDevice:
1366  case kCloneDir:
1367  case kDirectory:
1368  if (im == m_view->GetParent())
1369  DirSelectUp();
1370  else
1371  DirSelectDown();
1372  break;
1373 
1374  case kImageFile:
1375  case kVideoFile:
1376  StartSlideshow(kBrowseSlides); break;
1377  };
1378 }
1379 
1380 
1386 {
1387  QString err = m_mgr.ScanImagesAction(start);
1388  if (!err.isEmpty())
1389  ShowOkPopup(err);
1390 }
1391 
1392 
1398 {
1399  ImagePtrK selected = m_view->GetSelected();
1400  if (!selected)
1401  return;
1402 
1404  GallerySlideView *slide = new GallerySlideView(mainStack, "galleryslideview",
1405  m_editsAllowed);
1406  if (slide->Create())
1407  {
1408  mainStack->AddScreen(slide);
1409 
1410  // Update selected item when slideshow exits
1411  connect(slide, SIGNAL(ImageSelected(int)),
1412  this, SLOT(SelectImage(int)));
1413 
1414  if (selected->IsDirectory())
1415  // Show selected dir
1416  slide->Start(mode, selected->m_id);
1417  else
1418  // Show current dir starting at selection
1419  slide->Start(mode, selected->m_parentId, selected->m_id);
1420  }
1421  else
1422  delete slide;
1423 }
1424 
1425 
1430 {
1431  ImagePtrK im = m_view->GetParent();
1432  if (im)
1433  {
1434  LOG(VB_GUI, LOG_DEBUG, LOC +
1435  QString("Going up from %1").arg(im->m_filePath));
1436 
1437  // Select the upfolder in the higher dir
1438  m_view->Select(im->m_id);
1439 
1440  // Create tree rooted at parent of the kUpFolder directory node
1441  LoadData(im->m_parentId);
1442  }
1443  return true;
1444 }
1445 
1446 
1451 {
1452  ImagePtrK im = m_view->GetSelected();
1453  if (im)
1454  {
1455  LOG(VB_GUI, LOG_DEBUG, LOC +
1456  QString("Going down to %1").arg(im->m_filePath));
1457 
1458  // Create tree rooted at selected item
1459  LoadData(im->m_id);
1460  }
1461 }
1462 
1463 
1469 {
1470  ImagePtrK im = m_view->GetSelected();
1471  if (im)
1472  {
1473  // Mark/unmark selected item
1474  m_view->Mark(im->m_id, mark);
1475 
1476  // Redisplay buttonlist as a parent dir may have been unmarked
1477  BuildImageList();
1478  }
1479 }
1480 
1481 
1487 {
1488  if (mark)
1489  m_view->MarkAll();
1490  else
1491  m_view->ClearMarked();
1492 
1493  // Redisplay buttonlist
1494  BuildImageList();
1495 }
1496 
1497 
1502 {
1503  m_view->InvertMarked();
1504 
1505  // Redisplay buttonlist
1506  BuildImageList();
1507 }
1508 
1509 
1515 {
1516  ImagePtrK im = m_view->GetSelected();
1517  if (im && m_editsAllowed)
1518  {
1519  ImageIdList ids;
1520  ids.append(im->m_id);
1521  QString err = m_mgr.ChangeOrientation(transform, ids);
1522  if (!err.isEmpty())
1523  ShowOkPopup(err);
1524  }
1525 }
1526 
1527 
1533 {
1534  QString err = m_mgr.ChangeOrientation(transform, m_menuState.m_markedId);
1535  if (!err.isEmpty())
1536  ShowOkPopup(err);
1537 }
1538 
1539 
1545 {
1546  if (m_menuState.m_selected)
1547  {
1548  ImageIdList ids;
1549  ids.append(m_menuState.m_selected->m_id);
1550 
1551  QString err = m_mgr.HideFiles(hide, ids);
1552  if (!err.isEmpty())
1553 
1554  ShowOkPopup(err);
1555 
1556  else if (hide && !m_mgr.GetVisibility())
1557 
1558  // Unmark invisible file
1559  m_view->Mark(m_menuState.m_selected->m_id, false);
1560  }
1561 }
1562 
1563 
1569 {
1570  QString err = m_mgr.HideFiles(hide, m_menuState.m_markedId);
1571  if (!err.isEmpty())
1572 
1573  ShowOkPopup(err);
1574 
1575  else if (hide && !m_mgr.GetVisibility())
1576 
1577  // Unmark invisible files
1578  foreach (int id, m_menuState.m_markedId)
1579  m_view->Mark(id, false);
1580 }
1581 
1582 
1587 {
1588  if (m_menuState.m_selected)
1589  ShowDialog(tr("Do you want to delete\n%1 ?")
1590  .arg(m_menuState.m_selected->m_baseName), "ConfirmDelete");
1591 }
1592 
1593 
1598 {
1599  ShowDialog(tr("Do you want to delete all marked files ?"),
1600  "ConfirmDeleteMarked");
1601 }
1602 
1603 
1608 {
1609  // Show settings dialog
1612  StandardSettingDialog *ssd = new StandardSettingDialog(mainStack,
1613  "gallerysettings",
1614  config);
1615  if (!ssd->Create())
1616  {
1617  delete ssd;
1618  return;
1619  }
1620 
1621  mainStack->AddScreen(ssd);
1622 
1623  // Effect setting changes when dialog saves on exit
1624 
1625  connect(config, &GallerySettings::ClearDbPressed,
1627 
1628  connect(config, &GallerySettings::OrderChanged,
1629  this, [this]()
1630  {
1631  // Update db view, reset cover cache & reload
1632  int sortIm = gCoreContext->GetNumSetting("GalleryImageOrder");
1633  int sortDir = gCoreContext->GetNumSetting("GalleryDirOrder");
1634  m_mgr.SetSortOrder(sortIm, sortDir);
1635  m_view->ClearCache();
1637  });
1638 
1639  connect(config, &GallerySettings::DateChanged,
1640  this, [this]()
1641  {
1642  QString date = gCoreContext->GetSetting("GalleryDateFormat");
1643  m_mgr.SetDateFormat(date);
1644  BuildImageList();
1645  });
1646 
1647  connect(config, &GallerySettings::ExclusionsChanged,
1648  this, [this]()
1649  {
1650  // Request rescan
1651  QString exclusions = gCoreContext->GetSetting("GalleryIgnoreFilter");
1652  m_view->ClearCache();
1653  m_mgr.IgnoreDirs(exclusions);
1654  });
1655 }
1656 
1657 
1663 {
1664  gCoreContext->SaveBoolSetting("GalleryShowHidden", show);
1665 
1666  // Update Db(s)
1668 
1669  // Reset dir thumbnail cache
1670  m_view->ClearCache();;
1671 
1673 }
1674 
1675 
1681 void GalleryThumbView::ShowDialog(const QString& msg, const QString& event)
1682 {
1683  MythConfirmationDialog *popup =
1684  new MythConfirmationDialog(&m_popupStack, msg, true);
1685 
1686  if (popup->Create())
1687  {
1688  popup->SetReturnEvent(this, event);
1689  m_popupStack.AddScreen(popup);
1690  }
1691  else
1692  delete popup;
1693 }
1694 
1695 
1700 {
1701  if (m_menuState.m_selected)
1702  {
1703  QString base = QFileInfo(m_menuState.m_selected->m_baseName).completeBaseName();
1704  QString msg = tr("Enter a new name:");
1705  MythTextInputDialog *popup =
1706  new MythTextInputDialog(&m_popupStack, msg, FilterNone, false, base);
1707  if (popup->Create())
1708  {
1709  popup->SetReturnEvent(this, "FileRename");
1710  m_popupStack.AddScreen(popup);
1711  }
1712  else
1713  delete popup;
1714  }
1715 }
1716 
1717 
1722 {
1724 }
1725 
1726 
1731 {
1732  QString msg = tr("Enter password:");
1733  MythTextInputDialog *popup =
1734  new MythTextInputDialog(&m_popupStack, msg, FilterNone, true);
1735  if (popup->Create())
1736  {
1737  popup->SetReturnEvent(this, "Password");
1738  m_popupStack.AddScreen(popup);
1739  }
1740  else
1741  delete popup;
1742 }
1743 
1744 
1749 {
1750  gCoreContext->SaveSetting("GalleryShowType", type);
1751 
1752  // Update Db(s)
1753  m_mgr.SetType(type);
1754 
1755  // Reset dir thumbnail cache
1756  m_view->ClearCache();
1757 
1759 }
1760 
1761 
1767 {
1768  if (m_menuState.m_selected)
1769  {
1770  QString err = reset ? m_mgr.SetCover(m_menuState.m_selected->m_id, 0)
1771  : m_mgr.SetCover(m_menuState.m_selected->m_parentId,
1772  m_menuState.m_selected->m_id);
1773  if (!err.isEmpty())
1774  ShowOkPopup(err);
1775  }
1776 }
1777 
1778 
1783 {
1784  SelectZoomWidget(-1);
1785  BuildImageList();
1786 }
1787 
1788 
1793 {
1794  SelectZoomWidget(1);
1795  BuildImageList();
1796 }
1797 
1798 
1804 {
1805  m_zoomLevel += change;
1806 
1807  // constrain to zoom levels supported by theme
1808  if (m_zoomLevel < 0)
1809  m_zoomLevel = 0;
1810  if (m_zoomLevel >= m_zoomWidgets.size())
1811  m_zoomLevel = m_zoomWidgets.size() - 1;
1812 
1813  // Store any requested change, but not constraining adjustments
1814  // Thus, changing to a theme with fewer zoom levels will not overwrite the
1815  // setting
1816  if (change != 0)
1817  gCoreContext->SaveSetting("GalleryZoomLevel", m_zoomLevel);
1818 
1819  // dump the current list widget
1820  if (m_imageList)
1821  {
1822  m_imageList->SetVisible(false);
1823  disconnect(m_imageList, nullptr, this, nullptr);
1824  }
1825 
1826  // initialise new list widget
1828 
1829  m_imageList->SetVisible(true);
1831 
1832  // Monitor list actions (after focus events have been ignored)
1833  connect(m_imageList, SIGNAL(itemClicked(MythUIButtonListItem *)),
1835  connect(m_imageList, SIGNAL(itemSelected(MythUIButtonListItem *)),
1837 }
1838 
1839 
1844 {
1845  MythTextInputDialog *popup =
1846  new MythTextInputDialog(&m_popupStack, tr("Enter name of new directory"),
1847  FilterNone, false);
1848  if (popup->Create())
1849  {
1850  popup->SetReturnEvent(this, "MakeDir");
1851  m_popupStack.AddScreen(popup);
1852  }
1853  else
1854  delete popup;
1855 }
1856 
1857 
1862 {
1864  if (dir)
1865  m_mgr.CloseDevices(dir->m_device, true);
1866 }
1867 
1868 
1878 void GalleryThumbView::Copy(bool deleteAfter)
1879 {
1880  // Destination must be a dir
1881  ImagePtrK destDir = m_menuState.m_selected;
1882  if (!destDir || destDir->IsFile())
1883  return;
1884 
1885  // Use current markings, if any. Otherwise use previous markings
1886  ImageIdList markedIds = m_menuState.m_markedId;
1887  if (markedIds.isEmpty())
1888  {
1889  markedIds = m_menuState.m_prevMarkedId;
1890  if (markedIds.isEmpty())
1891  {
1892  ShowOkPopup(tr("No files specified"));
1893  return;
1894  }
1895  }
1896 
1897  // Get all files/dirs in subtree(s). Only files are copied
1898  ImageList files, dirs;
1899  m_mgr.GetDescendants(markedIds, files, dirs);
1900 
1901  if (dirs.isEmpty() && files.isEmpty())
1902  {
1903  ShowOkPopup(tr("No images"));
1904  // Nothing to clean up
1905  return;
1906  }
1907 
1908  // Child dirs appear before their subdirs. If no dirs, images are all direct children
1909  ImagePtrK aChild = dirs.isEmpty() ? files[0] : dirs[0];
1910 
1911  // Determine parent path including trailing /
1912  int basePathSize = aChild->m_filePath.size() - aChild->m_baseName.size();
1913 
1914  // Update filepaths for Db & generate URLs for filesystem copy
1915  // Only copy files, destination dirs will be created automatically
1916  TransferThread::TransferMap transfers;
1917  foreach(ImagePtr im, files)
1918  {
1919  // Replace base path with destination path
1920  im->m_filePath = ImageManagerFe::ConstructPath(destDir->m_filePath,
1921  im->m_filePath.mid(basePathSize));
1922 
1923  transfers.insert(im, m_mgr.BuildTransferUrl(im->m_filePath,
1924  destDir->IsLocal()));
1925  }
1926 
1927  // Create progress dialog
1928  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1930  new MythUIProgressDialog(tr("Copying files"), popupStack, "copydialog");
1931  if (progress->Create())
1932  popupStack->AddScreen(progress, false);
1933  else
1934  {
1935  delete progress;
1936  progress = nullptr;
1937  }
1938 
1939  // Copy files in a servant thread
1940  TransferThread copy(transfers, false, progress);
1942  TransferThread::ImageSet failed = copy.GetResult();
1943 
1944  if (progress)
1945  progress->Close();
1946 
1947  if (!failed.isEmpty())
1948  ShowOkPopup(tr("Failed to copy %L1/%Ln file(s)", nullptr, transfers.size())
1949  .arg(failed.size()));
1950 
1951  // Don't update Db for files that failed
1952  foreach (ImagePtrK im, failed)
1953  transfers.remove(im);
1954 
1955  ImageListK newImages = transfers.keys();
1956 
1957  // Include dirs
1958  QStringList dirPaths;
1959  foreach(ImagePtr im, dirs)
1960  {
1961  QString relPath = im->m_filePath.mid(basePathSize);
1962 
1963  dirPaths << relPath;
1964 
1965  // Replace base path with destination path
1966  im->m_filePath = ImageManagerFe::ConstructPath(destDir->m_filePath, relPath);
1967 
1968  // Append dirs so that hidden state & cover is preserved for new dirs
1969  // Pre-existing dirs will take precedance over these.
1970  newImages.append(im);
1971  }
1972 
1973  // Copy empty dirs as well (will fail for non-empty dirs)
1974  if (!dirPaths.isEmpty())
1975  m_mgr.MakeDir(destDir->m_id, dirPaths, false);
1976 
1977  if (!newImages.isEmpty())
1978  {
1979  // Update Db
1980  m_mgr.CreateImages(destDir->m_id, newImages);
1981 
1982  if (deleteAfter)
1983  {
1984  // Delete files/dirs that have been successfully copied
1985  // Will fail for dirs containing images that failed to copy
1986  ImageIdList ids;
1987  foreach (ImagePtrK im, newImages)
1988  ids << im->m_id;
1989 
1990  m_mgr.DeleteFiles(ids);
1991  }
1992  }
1993 }
1994 
1995 
2006 {
2007  // Destination must be a dir
2008  ImagePtrK destDir = m_menuState.m_selected;
2009  if (!destDir || destDir->IsFile())
2010  return;
2011 
2012  // Use current markings, if any. Otherwise use previous markings
2013  ImageIdList markedIds = m_menuState.m_markedId;
2014  if (markedIds.isEmpty())
2015  {
2016  markedIds = m_menuState.m_prevMarkedId;
2017  if (markedIds.isEmpty())
2018  {
2019  ShowOkPopup(tr("No files specified"));
2020  return;
2021  }
2022  }
2023 
2024  // Note UI mandates that transferees are either all local or all remote
2025  if (destDir->IsLocal() != ImageItem::IsLocalId(markedIds[0]))
2026  {
2027  // Moves between hosts require copy/delete
2028  Copy(true);
2029  return;
2030  }
2031 
2032  // Get marked images. Each file and dir will be renamed
2033  ImageList files, dirs;
2034  if (m_mgr.GetImages(markedIds, files, dirs) <= 0)
2035  {
2036  ShowOkPopup(tr("No images specified"));
2037  // Nothing to clean up
2038  return;
2039  }
2040  ImageList images = dirs + files;
2041 
2042  // Determine parent from first dir or pic
2043  ImagePtr aChild = images[0];
2044 
2045  // Determine parent path including trailing /
2046  // Note UI mandates that transferees all have same parent.
2047  int basePathSize = aChild->m_filePath.size() - aChild->m_baseName.size();
2048  QString parentPath = aChild->m_filePath.left(basePathSize);
2049 
2050  // Determine destination URLs
2051  TransferThread::TransferMap transfers;
2052  foreach(ImagePtrK im, images)
2053  {
2054  // Replace base path with destination path
2055  QString newPath = ImageManagerFe::ConstructPath(destDir->m_filePath,
2056  im->m_filePath.mid(basePathSize));
2057 
2058  transfers.insert(im, m_mgr.BuildTransferUrl(newPath, aChild->IsLocal()));
2059  }
2060 
2061  // Create progress dialog
2062  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
2064  new MythUIProgressDialog(tr("Moving files"), popupStack, "movedialog");
2065 
2066  if (progress->Create())
2067  popupStack->AddScreen(progress, false);
2068  else
2069  {
2070  delete progress;
2071  progress = nullptr;
2072  }
2073 
2074  // Move files in a servant thread
2075  TransferThread move(transfers, true, progress);
2076  WaitUntilDone(move);
2077  TransferThread::ImageSet failed = move.GetResult();
2078 
2079  if (progress)
2080  progress->Close();
2081 
2082  if (!failed.isEmpty())
2083  ShowOkPopup(tr("Failed to move %L1/%Ln file(s)", nullptr, transfers.size())
2084  .arg(failed.size()));
2085 
2086  // Don't update Db for files that failed
2087  foreach (ImagePtrK im, failed)
2088  transfers.remove(im);
2089 
2090  if (!transfers.isEmpty())
2091  {
2092  ImageListK moved = transfers.keys();
2093 
2094  // Unmark moved files
2095  foreach (ImagePtrK im, moved)
2096  m_view->Mark(im->m_id, false);
2097 
2098  // Update Db
2099  m_mgr.MoveDbImages(destDir, moved, parentPath);
2100  }
2101 }
2102 
2103 
2108 {
2109  QString path = m_mgr.CreateImport();
2110  if (path.isEmpty())
2111  {
2112  ShowOkPopup(tr("Failed to create temporary directory."));
2113  return;
2114  }
2115 
2116  // Replace placeholder in command
2117  QString cmd = gCoreContext->GetSetting("GalleryImportCmd");
2118  cmd.replace("%TMPDIR%", path);
2119 
2120  // Run command in a separate thread
2121  MythUIBusyDialog *busy =
2122  ShowBusyPopup(tr("Running Import command.\nPlease wait..."));
2123 
2124  ShellThread thread(cmd, path);
2125  WaitUntilDone(thread);
2126 
2127  if (busy)
2128  busy->Close();
2129 
2130  int error = thread.GetResult();
2131  if (error != 0)
2132  ShowOkPopup(tr("Import command failed.\nError: %1").arg(error));
2133 
2134  // Rescan local devices
2135  QString err = m_mgr.ScanImagesAction(true, true);
2136  if (!err.isEmpty())
2137  LOG(VB_GENERAL, LOG_ERR, LOC + err);
2138 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
ImageIdList m_markedId
Ids of all marked items.
Definition: galleryviews.h:65
void SetType(int showType)
Definition: imagemanager.h:404
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:294
void MenuPaste(MythMenu *)
Add a Paste submenu.
ImagePtrK m_selected
Selected item.
Definition: galleryviews.h:63
ImageManagerFe & m_mgr
Manages the images.
MenuSubjects m_menuState
Current selection/marked files when menu is invoked.
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
MythUIText * m_crumbsText
void RepeatOn(int on=1)
void UpdateImageItem(MythUIButtonListItem *)
Initialises a single buttonlist item.
def scan(profile, smoonURL, gate)
Definition: scan.py:43
void SetVisible(bool visible) override
MythUIText * m_typeFilterText
QPair< int, QString > ThumbPair
Definition: imagetypes.h:63
Dialog asking for user confirmation.
bool Select(int id, int fallback=0)
Selects first occurrence of an image.
void ShowDialog(const QString &, const QString &="")
Show a confirmation dialog.
Worker thread for running import.
QString ShortDateOf(const ImagePtrK &) const
Return a short datestamp for thumbnail captions.
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:485
QString objectName(void) const
Definition: mthread.cpp:254
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:66
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.
static bool MoveFile(const QString &src, const QString &dst, bool overwrite=false)
Definition: remotefile.cpp:677
void SaveSetting(const QString &key, int newValue)
static Type MythEventMessage
Definition: mythevent.h:66
static void error(const char *str,...)
Definition: vbi.c:42
The image manager for use by Frontends.
Definition: imagemanager.h:452
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.
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:136
QSet< int > m_thumbExists
Images where thumbnails are known to exist.
QList< MythUIButtonList * > m_zoomWidgets
Theme buttonlist widgets implementing zoom levels.
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
Definition: remotefile.cpp:587
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:402
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
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:185
QMap< ImagePtrK, QString > TransferMap
QString GetCachedThumbUrl(int id) const
Definition: galleryviews.h:118
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:167
GalleryThumbView(MythScreenStack *parent, const char *name)
Constructor.
bool m_editsAllowed
Edit privileges.
void UpdateThumbnail(MythUIButtonListItem *, const ImagePtrK &, const QString &url, int)
Update the buttonlist item with a thumbnail.
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)
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:16
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....
QString LongDateOf(const ImagePtrK &) const
Return a timestamp/datestamp for an image or dir.
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)
QString CreateImages(int, const ImageListK &images)
Copies database images (but not the files themselves).
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:84
void DeleteItem()
Confirm user deletion of an item.
void SetReturnEvent(QObject *retobject, const QString &resultid)
QString RenameFile(const ImagePtrK &im, const QString &name)
Rename an image.
int GetImages(const ImageIdList &ids, ImageList &files, ImageList &dirs) const
Returns images (local or remote but not a combination)
ImageIdList m_prevMarkedId
Ids of marked items in previous dir.
Definition: galleryviews.h:66
QString CheckThumbnail(MythUIButtonListItem *, const ImagePtrK &, ImageIdList &request, int)
Verify thumbnail is known to exist.
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:102
bool m_move
Copy if false, Move if true.
Filenames.
void ShowSettings()
Show configuration screen.
void MenuShow(MythMenu *)
Add a Show submenu.
const char * name
Definition: ParseText.cpp:328
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
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 ClearCache()
Clears UI cache.
Details not displayed.
Definition: galleryinfo.h:16
bool m_selectedMarked
Is selected item marked ?
Definition: galleryviews.h:64
void Update(const ImagePtrK &)
Populates available exif details for the current image/dir.
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.
int GetNumSetting(const QString &key, int defaultval=0)
void MenuMarked(MythMenu *)
Adds a Marking submenu.
Dialog prompting the user to enter a text string.
MythUIText * m_hideFilterText
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:172
bool Create(void) override
MythUIProgressDialog * m_dialog
Images for which copy/move failed.
void MenuTransform(MythMenu *)
Add a Transform submenu.
bool GetBoolSetting(const QString &key, bool defaultval=false)
MythUIText * m_positionText
void ZoomOut()
Use larger buttonlist widgets.
QSet< ImagePtrK > ImageSet
void Toggle(const ImagePtrK &)
Toggle infolist state for an image. Focusable widgets toggle between Basic & Full info....
Definition: galleryinfo.cpp:84
bool Create(void) override
QString MoveDbImages(const ImagePtrK &destDir, ImageListK &images, const QString &)
Moves database images (but not the files themselves).
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:202
bool m_unhiddenMarked
Is any marked item unhidden ?
Definition: galleryviews.h:69
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:492
Worker thread for copying/moving files.
MythUIText * m_emptyText
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:269
#define PHOTO_DB_ID
Definition: imagetypes.h:28
Storage Group and local mounted media.
Definition: imagetypes.h:35
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
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.
void SaveBoolSetting(const QString &key, bool newValue)
const QString & Message() const
Definition: mythevent.h:58
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:407
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:68
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:132
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:60
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:67
void MenuAction(MythMenu *)
Add a Action submenu.
void SetVisibility(bool showHidden)
Definition: imagemanager.h:410
void RemoveFromCacheByFile(const QString &fname)
ShellThread(QString cmd, QString path)
bool Create(void) override
A device sub dir comprised from multiple SG dirs.
Definition: imagetypes.h:36