Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
BrowseDialog.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
11 /* This is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This software is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public
22  * License along with this software. If not, see
23  * <http://www.gnu.org/licenses/>.
24  *
25  */
26 
27 #include "hugin/BrowseDialog.h"
28 #include "hugin/huginApp.h"
29 #include "wx/dir.h"
30 #include "wx/busyinfo.h"
32 #include "hugin/MainFrame.h"
33 #include "base_wx/wxcms.h"
34 #include "base_wx/LensTools.h"
35 #include "base_wx/platform.h"
36 #include "base_wx/wxutils.h"
37 
38 wxDECLARE_EVENT(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE, wxCommandEvent);
39 wxDEFINE_EVENT(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE, wxCommandEvent);
40 
42 class ThreadImage : public wxObject
43 {
44 public:
45  // explicit disable default contructor
46  ThreadImage() = delete;
47  // construct wxImage with given vigra image classes
48  ThreadImage(vigra::BRGBImage* thumbnail, vigra::BImage* mask)
49  {
50  // save pointer to raw image data for cleanup in destructor
51  m_preview = thumbnail;
52  m_mask = mask;
53  // construct wxImage, we keep track of the raw data
54  m_image=new wxImage(m_preview->width(), m_preview->height(), (unsigned char*)m_preview->data(), (unsigned char*)m_mask->data(), true);
55  }
56  // clean up
58  {
59  delete m_image;
60  delete m_preview;
61  delete m_mask;
62  }
63  // return pointer to wxImage
64  wxImage* GetwxImage() const { return m_image; }
65 private:
66  wxImage* m_image;
67  vigra::BRGBImage* m_preview;
68  vigra::BImage* m_mask;
69 };
70 
72 class ThumbnailThread : public wxThread
73 {
74 public:
75  ThumbnailThread(BrowsePTOFilesDialog* parentDialog, const wxArrayString& fileList, const wxSize size)
76  : wxThread(wxTHREAD_DETACHED)
77  {
78  m_parentDialog = parentDialog;
79  m_files = fileList;
80  m_thumbnailSize = size;
81  }
83  {
84  wxCriticalSectionLocker enter(m_parentDialog->m_ThreadCS);
85  // the thread is being destroyed; make sure not to leave dangling pointers around
87  }
88 
89 protected:
90  // main worker thread
91  virtual ExitCode Entry()
92  {
93  for (int i = 0; i < m_files.size(); ++i)
94  {
95  // generate thumbnail
96  vigra::BRGBImage* preview=new vigra::BRGBImage();
97  vigra::BImage* mask = new vigra::BImage();
98  vigra::ImageImportInfo::ICCProfile iccProfile;
99  if (!GenerateThumbnail(m_files[i].ToStdString(), m_thumbnailSize, *preview, *mask, iccProfile))
100  {
101  // generation of thumbnail failed, skip file
102  delete preview;
103  delete mask;
104  continue;
105  };
106  if (TestDestroy())
107  {
108  break;
109  };
110  // fit generated thumbnail into the full thumbnail rectangle
111  if (m_thumbnailSize.GetWidth() != preview->size().width() || m_thumbnailSize.GetHeight() != preview->size().height())
112  {
113  vigra::BRGBImage* scaledPreview = new vigra::BRGBImage(vigra::Diff2D(m_thumbnailSize.GetWidth(), m_thumbnailSize.GetHeight()), vigra::RGBValue<vigra::UInt8>(255, 255, 255));
114  vigra::BImage* scaledMask = new vigra::BImage(scaledPreview->size(), vigra::UInt8(0));
115  vigra::Point2D offset((scaledPreview->width() - preview->width()) / 2, (scaledPreview->height() - preview->height()) / 2);
116  vigra::copyImage(vigra::srcImageRange(*preview), vigra::destImage(*scaledPreview, offset));
117  vigra::copyImage(vigra::srcImageRange(*mask), vigra::destImage(*scaledMask, offset));
118  delete preview;
119  delete mask;
120  preview = scaledPreview;
121  mask = scaledMask;
122  }
123  // convert vigra image to wxImage
124  ThreadImage* thumbnailImageData = new ThreadImage(preview, mask);
125  // notify dialog about new available thumbnail
126  wxCommandEvent* event = new wxCommandEvent(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE);
127  event->SetInt(i);
128  event->SetEventObject(thumbnailImageData);
129  // check if we need to cancel
130  if (TestDestroy())
131  {
132  // we are destroying the thread
133  // clean up all unneeded pointer
134  delete event;
135  delete thumbnailImageData;
136  break;
137  }
138  else
139  {
140  // all ok, notify dialog to update its image list
141  wxQueueEvent(m_parentDialog, event);
142  };
143  };
144  return (wxThread::ExitCode)0;
145  }
146 
148  wxArrayString m_files;
150 };
151 
152 #define THUMBNAIL_SIZE 128
153 
154 BrowsePTOFilesDialog::BrowsePTOFilesDialog(wxWindow *parent, const wxString startDirectory)
155 {
156  // load our children. some children might need special
157  // initialization. this will be done later.
158  wxXmlResource::Get()->LoadDialog(this, parent, "browse_pto_dialog");
159 
160  m_dirCtrl = XRCCTRL(*this, "browse_dirctrl", wxGenericDirCtrl);
161  m_dirCtrl->Bind(wxEVT_DIRCTRL_SELECTIONCHANGED, &BrowsePTOFilesDialog::OnDirectoryChanged, this);
162  m_listCtrl = XRCCTRL(*this, "browse_listctrl", wxListCtrl);
163  m_listCtrl->Bind(wxEVT_LIST_ITEM_SELECTED, &BrowsePTOFilesDialog::OnFileChanged, this);
164  m_listCtrl->Bind(wxEVT_LIST_COL_CLICK, &BrowsePTOFilesDialog::OnListColClick, this);
165  m_previewCtrl = XRCCTRL(*this, "browse_preview", wxStaticBitmap);
166  m_splitter1 = XRCCTRL(*this, "browse_splitter1", wxSplitterWindow);
167  m_splitter2 = XRCCTRL(*this, "browse_splitter2", wxSplitterWindow);
168  m_showMap = XRCCTRL(*this, "browse_show_map", wxButton);
169  m_labelControl = XRCCTRL(*this, "browse_statictext", wxStaticText);
170  m_labelControl->SetFont(m_labelControl->GetFont().Larger().Larger());
171  m_thumbnails.Create(THUMBNAIL_SIZE, THUMBNAIL_SIZE, true, 0);
172  m_listCtrl->SetWindowStyle(wxLC_REPORT | wxLC_AUTOARRANGE | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES);
173  m_listCtrl->SetImageList(&m_thumbnails, wxIMAGE_LIST_NORMAL);
174  m_listCtrl->Bind(wxEVT_LEFT_DCLICK, &BrowsePTOFilesDialog::OnDblClickListCtrl, this);
175  m_listCtrl->InsertColumn(0, _("Filename"), wxLIST_FORMAT_LEFT, 300);
176  m_listCtrl->InsertColumn(1, _("# images"), wxLIST_FORMAT_RIGHT, 50);
177  m_listCtrl->InsertColumn(2, _("# active images"), wxLIST_FORMAT_RIGHT, 50);
178  m_listCtrl->InsertColumn(3, _("Projection"), wxLIST_FORMAT_LEFT, 200);
179  m_listCtrl->InsertColumn(4, _("Field of view"), wxLIST_FORMAT_LEFT, 100);
180  m_listCtrl->InsertColumn(5, _("Canvas size"), wxLIST_FORMAT_LEFT, 100);
181  m_listCtrl->InsertColumn(6, _("Model"), wxLIST_FORMAT_LEFT, 200);
182  m_listCtrl->InsertColumn(7, _("Lens"), wxLIST_FORMAT_LEFT, 250);
183  m_listCtrl->InsertColumn(8, _("Focal length"), wxLIST_FORMAT_LEFT, 150);
184  m_listCtrl->InsertColumn(9, _("Capture date"), wxLIST_FORMAT_LEFT, 150);
185  m_listCtrl->InsertColumn(10, _("Duration"), wxLIST_FORMAT_LEFT, 50);
186  m_listType = XRCCTRL(*this, "browse_list_type", wxChoice);
187  m_listType->Bind(wxEVT_CHOICE, &BrowsePTOFilesDialog::OnListTypeChanged, this);
188  // restore some settings
189  RestoreFramePosition(this, "BrowsePTODialog");
190  wxConfigBase* config = wxConfigBase::Get();
191  //splitter position
192  int splitter_pos = config->Read("/BrowsePTODialog/splitterPos1", -1l);
193  if (splitter_pos > 0)
194  {
195  m_splitter1->SetSashPosition(splitter_pos);
196  };
197  splitter_pos = config->Read("/BrowsePTODialog/splitterPos2", -1l);
198  if (splitter_pos > 0)
199  {
200  m_splitter2->SetSashPosition(splitter_pos);
201  };
202  //get saved width
203  for (int j = 0; j < m_listCtrl->GetColumnCount(); j++)
204  {
205  // -1 is auto
206  int width = config->Read(wxString::Format("/BrowsePTODialog/ColumnWidth%d", j), -1);
207  if (width != -1 && width > 5)
208  {
209  m_listCtrl->SetColumnWidth(j, width);
210  };
211  };
212  m_sortCol = config->Read("/BrowsePTODialog/SortColumn", -1);
213  m_sortAscending = config->Read("/BrowsePTODialog/SortAscending", 1) == 1 ? true : false;
214  if (m_sortCol != -1)
215  {
216  m_listCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
217  };
218 
219  // fill values for start directory
220  if (!startDirectory.IsEmpty())
221  {
222  m_dirCtrl->SetPath(startDirectory);
223  };
224  long listType = config->Read("/BrowsePTODialog/ListType", 0l);
225  m_listType->SetSelection(listType);
226  wxCommandEvent event;
227  event.SetInt(listType);
228  OnListTypeChanged(event);
229  Bind(wxEVT_BUTTON, &BrowsePTOFilesDialog::OnOk, this, wxID_OK);
230  Bind(wxEVT_BUTTON, &BrowsePTOFilesDialog::OnShowOnMap, this, XRCID("browse_show_map"));
231  Bind(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE, &BrowsePTOFilesDialog::OnThumbnailUpdate, this);
232 };
233 
235 {
236  // stop working thread
238  // save some settings
239  StoreFramePosition(this, "BrowsePTODialog");
240  wxConfigBase* config = wxConfigBase::Get();
241  config->Write("/BrowsePTODialog/splitterPos1", m_splitter1->GetSashPosition());
242  config->Write("/BrowsePTODialog/splitterPos2", m_splitter2->GetSashPosition());
243  // save width of all columns in wxListCtrl
244  for (int j = 0; j < m_listCtrl->GetColumnCount(); j++)
245  {
246  config->Write(wxString::Format("/BrowsePTODialog/ColumnWidth%d", j), m_listCtrl->GetColumnWidth(j));
247  };
248  config->Write("/BrowsePTODialog/ListType", m_listType->GetSelection());
249  config->Write(wxT("/BrowsePTODialog/SortColumn"), m_sortCol);
250  config->Write(wxT("/BrowsePTODialog/SortAscending"), m_sortAscending ? 1 : 0);
251  config->Flush();
252 }
253 
255 {
256  long index = -1;
257  if (m_listCtrl->GetSelectedItemCount() == 1)
258  {
259  index = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
260  };
261  if (index >= 0 && index < m_ptoInfo.size())
262  {
263  return m_ptoInfo[TranslateIndex(index)].ptoFilename.GetFullPath();
264  }
265  else
266  {
267  return wxEmptyString;
268  };
269 }
270 
272 {
273  return m_dirCtrl->GetPath();
274 }
275 
276 void BrowsePTOFilesDialog::OnOk(wxCommandEvent& e)
277 {
278  if (m_listCtrl->GetSelectedItemCount() == 1)
279  {
280  EndModal(wxID_OK);
281  }
282  else
283  {
284  wxBell();
285  }
286 }
287 
289 {
290  PanoInfo info;
291  info.ptoFilename = file;
292  // read pto file
293  HuginBase::Panorama pano;
294  const std::string input(file.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
295  if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input)))
296  {
297  return info;
298  };
299  // fill class to store some information about the pano
300  info.nrImages = pano.getNrOfImages();
301  info.nrActiveImages = pano.getActiveImages().size();
302  info.options = pano.getOptions();
303  // translate projection name
304  {
305  pano_projection_features proj;
306  wxString s;
307  if (panoProjectionFeaturesQuery(info.options.getProjection(), &proj))
308  {
309  wxString str2(proj.name, wxConvLocal);
310  info.projectionName = wxGetTranslation(str2);
311  };
312  };
313  // read some EXIF data
314  const HuginBase::UIntSet activeImages = pano.getActiveImages();
315  if (!activeImages.empty())
316  {
317  HuginBase::SrcPanoImage img0 = pano.getSrcImage(*activeImages.begin());
318  if(!hugin_utils::FileExists(img0.getFilename()))
319  {
320  return info;
321  }
322  img0.readEXIF();
323  info.camera = img0.getExifModel();
324  info.lens = img0.getExifLens();
325  info.focalLength35 = img0.getExifFocalLength35();
326  if (info.focalLength35 < 0.01 && img0.getCropFactor()>0)
327  {
328  info.focalLength35 = img0.getExifFocalLength() * img0.getCropFactor();
329  };
330  if (info.focalLength35 < 0.01)
331  {
332  info.focalLength35 = HuginBase::SrcPanoImage::calcFocalLength(img0.getProjection(), img0.getHFOV(), 1.0, img0.getSize());
333  };
335  // read date/time of all active images
336  if (!img0.getExifDate().empty())
337  {
338  struct tm exifdatetime;
339  if (img0.getExifDateTime(&exifdatetime) == 0)
340  {
341  info.start = wxDateTime(exifdatetime);
342  info.end = info.start;
343  };
344  // iterate all images, except the first one, this image is already handled before
345  HuginBase::UIntSet imgs(activeImages);
346  imgs.erase(imgs.begin());
347  for (const auto& img : imgs)
348  {
349  HuginBase::SrcPanoImage imgSrcImage = pano.getSrcImage(img);
350  if (!hugin_utils::FileExists(imgSrcImage.getFilename()))
351  {
352  continue;
353  };
354  imgSrcImage.readEXIF();
355  memset(&exifdatetime, 0, sizeof(exifdatetime));
356  if (!imgSrcImage.getExifDate().empty() && imgSrcImage.getExifDateTime(&exifdatetime) == 0)
357  {
358  const wxDateTime dateTime = wxDateTime(exifdatetime);
359  if (info.start.IsValid())
360  {
361  if (dateTime.IsEarlierThan(info.start))
362  {
363  info.start = dateTime;
364  }
365  if (dateTime.IsLaterThan(info.end))
366  {
367  info.end = dateTime;
368  }
369  }
370  else
371  {
372  info.start = dateTime;
373  info.end = dateTime;
374  }
375  }
376  }
377  if (info.start.IsValid())
378  {
379  info.duration = info.end.Subtract(info.start);
380  }
381  };
382  const auto& fileMetadata = img0.getFileMetadata();
383  const auto& latitude = fileMetadata.find("latitude");
384  const auto& longitude = fileMetadata.find("longitude");
385  if (latitude != fileMetadata.end())
386  {
387  hugin_utils::stringToDouble(latitude->second, info.GPSLatitude);
388  };
389  if (longitude != fileMetadata.end())
390  {
391  hugin_utils::stringToDouble(longitude->second, info.GPSLongitude);
392  };
393  };
394  return info;
395 }
396 
398 {
399 #ifndef __WXMSW__
400  // in generic implementation we can add text to column only in report view
401  if (m_listCtrl->InReportView())
402 #endif
403  {
404  m_listCtrl->SetItem(index, 1, wxString::Format("%zu", info.nrImages));
405  m_listCtrl->SetItem(index, 2, wxString::Format("%zu", info.nrActiveImages));
406  m_listCtrl->SetItem(index, 3, info.projectionName);
407  m_listCtrl->SetItem(index, 4, wxString::Format("%.0f x %.0f", info.options.getHFOV(), info.options.getVFOV()));
408  m_listCtrl->SetItem(index, 5, wxString::Format("%u x %u", info.options.getWidth(), info.options.getHeight()));
409  m_listCtrl->SetItem(index, 6, info.camera);
410  m_listCtrl->SetItem(index, 7, info.lens);
411  m_listCtrl->SetItem(index, 8, info.focalLengthString);
412  if (info.start.IsValid())
413  {
414  m_listCtrl->SetItem(index, 9, info.start.Format());
415  };
416  m_listCtrl->SetItem(index, 10, GetFormattedTimeSpan(info.duration));
417  };
418 }
419 
421 {
422 #ifndef __WXMSW__
423  m_listCtrl->DeleteAllItems();
424 #endif
425  m_listCtrl->SetWindowStyle(newStyle);
426 #ifndef __WXMSW__
427  // changing the window style does invalidate the items, so we delete all items
428  // and add then again
429  for (size_t i = 0; i < m_ptoInfo.size(); ++i)
430  {
431  m_listCtrl->InsertItem(i, m_ptoInfo[i].ptoFilename.GetFullName(), -1);
432  m_listCtrl->SetItemData(i, i);
433  FillPanoInfo(m_ptoInfo[i], i);
434  };
435  SortItems();
436 #endif
437 }
438 
440 {
441  if (m_dirCtrl && m_listCtrl)
442  {
443  wxWindowDisabler disableAll;
444  wxBusyInfo busy(wxBusyInfoFlags().Icon(MainFrame::Get()->GetIcon()).Label(wxString::Format(_("Reading directory %s"), m_dirCtrl->GetPath().c_str())));
445  // stop running thumbnail thread from last directory if not finished yet
447  // find all pto files in directory
448  m_ptoInfo.clear();
449  m_thumbnails.RemoveAll();
450  SetTitle(wxString::Format(_("Browse project files in %s"), m_dirCtrl->GetPath().c_str()));
451  wxArrayString files;
452  wxDir::GetAllFiles(m_dirCtrl->GetPath(), &files, "*.pto", wxDIR_FILES | wxDIR_HIDDEN | wxDIR_NO_FOLLOW);
453  // add to wxListCtrl
454  m_listCtrl->DeleteAllItems();
455  m_ptoInfo.resize(files.size());
456  for (size_t i = 0; i < files.size(); ++i)
457  {
458  const wxFileName filename(files[i]);
459  m_ptoInfo[i] = ParsePTOFile(filename);
460  m_listCtrl->InsertItem(i, m_ptoInfo[i].ptoFilename.GetFullName(), -1);
461  m_listCtrl->SetItemData(i, i);
462  FillPanoInfo(m_ptoInfo[i], i);
463  };
464  SortItems();
465  // start background thread for creating all thumbnails
466  m_thumbnailThread = new ThumbnailThread(this, files, m_thumbnails.GetSize());
467  if (!m_listCtrl->InReportView())
468  {
469  m_thumbnailThread->Run();
470  };
471  // clear preview area
472  m_previewCtrl->SetBitmap(wxBitmap());
473  m_previewCtrl->Show(true);
474  m_labelControl->Show(false);
475  m_showMap->Show(false);
476  m_showMap->Enable(false);
477  m_showMap->GetParent()->Layout();
478  };
479 }
480 
482 {
483  if (e.GetIndex() >= 0)
484  {
485  GeneratePreview(TranslateIndex(e.GetIndex()));
486  };
487 }
488 
490 {
491  if (m_listCtrl->GetSelectedItemCount() == 1)
492  {
493  EndModal(wxID_OK);
494  };
495 }
496 
498 {
499  const int newCol = e.GetColumn();
500  if (m_sortCol == newCol)
501  {
503  }
504  else
505  {
506  m_sortCol = newCol;
507  m_sortAscending = true;
508  };
509  m_listCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
510  SortItems();
511  Refresh();
512 }
513 
515 {
516  // we have created a new thumbnail, make the wxListCtrl aware of it
517  const int index = e.GetInt();
518  ThreadImage* thumbnail= wxDynamicCast(e.GetEventObject(), ThreadImage);
519  m_ptoInfo[index].imageIndex = m_thumbnails.Add(*(thumbnail->GetwxImage()));
520  for (size_t i = 0; i < m_listCtrl->GetItemCount(); ++i)
521  {
522  if (m_listCtrl->GetItemData(i) == index)
523  {
524  m_listCtrl->SetItemImage(i, m_ptoInfo[index].imageIndex);
525  break;
526  };
527  };
528  delete thumbnail;
529 #ifndef __WXMSW__
530  // a simple Refresh for repainting the control is not working
531  // all thumbnails are drawn on top of each other
532  // only when all done after a size event all is drawn correctly
533  m_listCtrl->SendSizeEvent();
534 #endif
535 }
536 
538 {
539  if (e.GetSelection() == 0)
540  {
541  UpdateItemTexts(wxLC_ICON | wxLC_AUTOARRANGE | wxLC_SINGLE_SEL);
542  // start creating thumbnail images in background thread
543  if (m_thumbnailThread && !m_thumbnailThread->IsRunning())
544  {
545  m_thumbnailThread->Run();
546  };
547  }
548  else
549  {
550  UpdateItemTexts(wxLC_REPORT | wxLC_AUTOARRANGE | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES);
551  };
552 #ifndef __WXMSW__
553  // a simple Refresh for repainting the control is not working
554  // all thumbnails are drawn on top of each other
555  // only when all done after a size event all is drawn correctly
556  m_listCtrl->SendSizeEvent();
557 #endif
558 }
559 
560 void BrowsePTOFilesDialog::OnShowOnMap(wxCommandEvent& e)
561 {
562  long index = -1;
563  if (m_listCtrl->GetSelectedItemCount() == 1)
564  {
565  index = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
566  };
567  if (index >= 0 && index < m_ptoInfo.size())
568  {
569  if (m_ptoInfo[index].HasGPS())
570  {
571  const wxString openstreetMapLink = "https://www.openstreetmap.org/?mlat=" + wxString::FromCDouble(m_ptoInfo[index].GPSLatitude) + "&mlon=" + wxString::FromCDouble(m_ptoInfo[index].GPSLongitude);
572  wxLaunchDefaultBrowser(openstreetMapLink);
573  }
574  }
575 }
576 
578 {
579  return m_listCtrl->GetItemData(index);
580 }
581 
582 template <class Type>
583 int GreaterComparisonOperator(const Type& a, const Type& b)
584 {
585  if (a > b)
586  {
587  return 1;
588  }
589  else
590  {
591  if (a < b)
592  {
593  return -1;
594  }
595  else {
596  return 0;
597  };
598  }
599 }
600 
601 template <class Type>
602 int SmallerComparisonOperator(const Type& a, const Type& b)
603 {
604  if (a < b)
605  {
606  return 1;
607  }
608  else
609  {
610  if (a > b)
611  {
612  return -1;
613  }
614  else {
615  return 0;
616  };
617  }
618 }
619 
620 #define SORTASCENDING(functionName, var) \
621 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
622 {\
623  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
624  return GreaterComparisonOperator(data->at(item1).var, data->at(item2).var);\
625 }
626 #define SORTDESCENDING(functionName, var) \
627 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
628 {\
629  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
630  return SmallerComparisonOperator(data->at(item1).var, data->at(item2).var);\
631 }
632 
633 SORTASCENDING(SortFilenameAscending, ptoFilename.GetFullName())
634 SORTDESCENDING(SortFilenameDescending, ptoFilename.GetFullName())
635 SORTASCENDING(SortNrImagesAscending, nrImages)
636 SORTDESCENDING(SortNrImagesDescending, nrImages)
637 SORTASCENDING(SortNrActiveImagesAscending, nrActiveImages)
638 SORTDESCENDING(SortNrActiveImagesDescending, nrActiveImages)
639 SORTASCENDING(SortProjectionAscending, projectionName)
640 SORTDESCENDING(SortProjectionDescending, projectionName)
641 SORTASCENDING(SortCanvasSizeAcending, options.getSize().area())
642 SORTDESCENDING(SortCanvasSizeDescending, options.getSize().area())
643 SORTASCENDING(SortFocalLengthAscending, focalLength35)
644 SORTDESCENDING(SortFocalLengthDescending, focalLength35)
645 
646 #undef SORTASCENDING
647 #undef SORTDESCENDING
648 
649 // special variant for wxStrings
650 #define SORTASCENDING(functionName, var) \
651 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
652 {\
653  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
654  return data->at(item1).var.CmpNoCase(data->at(item2).var);\
655 }
656 #define SORTDESCENDING(functionName, var) \
657 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
658 {\
659  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
660  return -(data->at(item1).var.CmpNoCase(data->at(item2).var));\
661 }
662 SORTASCENDING(SortCameraAscending, camera)
663 SORTDESCENDING(SortCameraDescending, camera)
664 SORTASCENDING(SortLensAscending, lens)
665 SORTDESCENDING(SortLensDescending, lens)
666 
667 #undef SORTASCENDING
668 #undef SORTDESCENDING
669 
670 // sort by field of view
671 int wxCALLBACK SortFieldOfViewAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
672 {
673  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
674  const float fieldOfView1 = data->at(item1).options.getHFOV() * 1000 + data->at(item1).options.getVFOV();
675  const float fieldOfView2 = data->at(item2).options.getHFOV() * 1000 + data->at(item2).options.getVFOV();
676  return GreaterComparisonOperator(fieldOfView1, fieldOfView2);
677 }
678 
679 int wxCALLBACK SortFieldOfViewDescending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
680 {
681  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
682  const float fieldOfView1 = data->at(item1).options.getHFOV() * 1000 + data->at(item1).options.getVFOV();
683  const float fieldOfView2 = data->at(item2).options.getHFOV() * 1000 + data->at(item2).options.getVFOV();
684  return SmallerComparisonOperator(fieldOfView1, fieldOfView2);
685 }
686 
687 // sort by date
688 int wxCALLBACK SortDateAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
689 {
690  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
691  if (data->at(item1).start.IsLaterThan(data->at(item2).start))
692  {
693  return 1;
694  }
695  if (data->at(item1).start.IsEarlierThan(data->at(item2).start))
696  {
697  return -1;
698  }
699  return 0;
700 };
701 
702 int wxCALLBACK SortDateDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
703 {
704  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
705  if (data->at(item1).start.IsEarlierThan(data->at(item2).start))
706  {
707  return 1;
708  }
709  if (data->at(item1).start.IsLaterThan(data->at(item2).start))
710  {
711  return -1;
712  }
713  return 0;
714 };
715 
716 // sort by duration
717 int wxCALLBACK SortDurationAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
718 {
719  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
720  if (data->at(item1).duration.IsLongerThan(data->at(item2).duration))
721  {
722  return 1;
723  }
724  if (data->at(item1).duration.IsShorterThan(data->at(item2).duration))
725  {
726  return -1;
727  }
728  return 0;
729 };
730 
731 int wxCALLBACK SortDurationDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
732 {
733  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
734  if (data->at(item1).duration.IsShorterThan(data->at(item2).duration))
735  {
736  return 1;
737  }
738  if (data->at(item1).duration.IsLongerThan(data->at(item2).duration))
739  {
740  return -1;
741  }
742  return 0;
743 };
744 
746 {
747  if (m_sortCol > -1)
748  {
749  switch (m_sortCol)
750  {
751  default:
752  case 0: // filename
753  if (m_sortAscending)
754  {
755  m_listCtrl->SortItems(SortFilenameAscending, wxIntPtr(&m_ptoInfo));
756  }
757  else
758  {
759  m_listCtrl->SortItems(SortFilenameDescending, wxIntPtr(&m_ptoInfo));
760  };
761  break;
762  case 1: // number of images
763  if (m_sortAscending)
764  {
765  m_listCtrl->SortItems(SortNrImagesAscending, wxIntPtr(&m_ptoInfo));
766  }
767  else
768  {
769  m_listCtrl->SortItems(SortNrImagesDescending, wxIntPtr(&m_ptoInfo));
770  };
771  break;
772  case 2: // number of active images
773  if (m_sortAscending)
774  {
775  m_listCtrl->SortItems(SortNrActiveImagesAscending, wxIntPtr(&m_ptoInfo));
776  }
777  else
778  {
779  m_listCtrl->SortItems(SortNrActiveImagesDescending, wxIntPtr(&m_ptoInfo));
780  };
781  break;
782  case 3: // projection
783  if (m_sortAscending)
784  {
785  m_listCtrl->SortItems(SortProjectionAscending, wxIntPtr(&m_ptoInfo));
786  }
787  else
788  {
789  m_listCtrl->SortItems(SortProjectionDescending, wxIntPtr(&m_ptoInfo));
790  };
791  break;
792  case 4: // field of view
793  if (m_sortAscending)
794  {
795  m_listCtrl->SortItems(SortFieldOfViewAscending, wxIntPtr(&m_ptoInfo));
796  }
797  else
798  {
799  m_listCtrl->SortItems(SortFieldOfViewDescending, wxIntPtr(&m_ptoInfo));
800  };
801  break;
802  case 5: // canvas size
803  if (m_sortAscending)
804  {
805  m_listCtrl->SortItems(SortCanvasSizeAcending, wxIntPtr(&m_ptoInfo));
806  }
807  else
808  {
809  m_listCtrl->SortItems(SortCanvasSizeDescending, wxIntPtr(&m_ptoInfo));
810  };
811  break;
812  case 6: // model
813  if (m_sortAscending)
814  {
815  m_listCtrl->SortItems(SortCameraAscending, wxIntPtr(&m_ptoInfo));
816  }
817  else
818  {
819  m_listCtrl->SortItems(SortCameraDescending, wxIntPtr(&m_ptoInfo));
820  };
821  break;
822  case 7: // lens
823  if (m_sortAscending)
824  {
825  m_listCtrl->SortItems(SortLensAscending, wxIntPtr(&m_ptoInfo));
826  }
827  else
828  {
829  m_listCtrl->SortItems(SortLensDescending, wxIntPtr(&m_ptoInfo));
830  };
831  break;
832  case 8: // focal length
833  if (m_sortAscending)
834  {
835  m_listCtrl->SortItems(SortFocalLengthAscending, wxIntPtr(&m_ptoInfo));
836  }
837  else
838  {
839  m_listCtrl->SortItems(SortFocalLengthDescending, wxIntPtr(&m_ptoInfo));
840  };
841  break;
842  case 9: // capture date
843  if (m_sortAscending)
844  {
845  m_listCtrl->SortItems(SortDateAscending, wxIntPtr(&m_ptoInfo));
846  }
847  else
848  {
849  m_listCtrl->SortItems(SortDateDesending, wxIntPtr(&m_ptoInfo));
850  };
851  break;
852  case 10: // duration
853  if (m_sortAscending)
854  {
855  m_listCtrl->SortItems(SortDurationAscending, wxIntPtr(&m_ptoInfo));
856  }
857  else
858  {
859  m_listCtrl->SortItems(SortDurationDesending, wxIntPtr(&m_ptoInfo));
860  };
861  break;
862  };
863  };
864 };
865 
867 {
868  // delete running background thread
869  wxBusyCursor busy;
870  {
871  bool deleteStoppedThread = false;
872  {
873  wxCriticalSectionLocker enter(m_ThreadCS);
874  if (m_thumbnailThread)
875  {
876  if (m_thumbnailThread->IsRunning())
877  {
878  m_thumbnailThread->Delete();
879  }
880  else
881  {
882  deleteStoppedThread = true;
883  };
884  };
885  };
886  if (deleteStoppedThread)
887  {
888  delete m_thumbnailThread;
889  };
890  };
891  // exit from the critical section to give the thread
892  // the possibility to enter its destructor
893  // (which is guarded with m_ThreadCS critical section!)
894  while (1)
895  {
896  {
897  wxCriticalSectionLocker enter(m_ThreadCS);
898  if (!m_thumbnailThread)
899  {
900  break;
901  };
902  };
903  // wait for thread completion
904  wxThread::This()->Sleep(1);
905  };
906 }
907 
909 {
910  if (index<0 || index>m_ptoInfo.size())
911  {
912  return;
913  };
914  wxWindowDisabler disableAll;
915  wxBusyInfo busyInfo(wxBusyInfoFlags().Icon(MainFrame::Get()->GetIcon()).Label(wxString::Format(_("Generating preview for %s"), m_ptoInfo[index].ptoFilename.GetFullName().c_str())));
916  wxSize previewSize = m_previewCtrl->GetParent()->GetSize();
917  vigra::BRGBImage preview;
918  vigra::BImage mask;
919  vigra::ImageImportInfo::ICCProfile iccProfile;
920  if (GenerateThumbnail(m_ptoInfo[index].ptoFilename.GetFullPath().ToStdString(), previewSize, preview, mask, iccProfile))
921  {
922  wxImage image(preview.width(), preview.height(), (unsigned char*)preview.data(), (unsigned char*)mask.data(), true);
923  // now apply color profile
924  if (!iccProfile.empty() || huginApp::Get()->HasMonitorProfile())
925  {
927  };
928  // now show in GUI
929  m_previewCtrl->SetBitmap(image);
930  m_previewCtrl->Show(true);
931  m_labelControl->Show(false);
932  m_showMap->Show(m_ptoInfo[index].HasGPS());
933  m_showMap->Enable(m_ptoInfo[index].HasGPS());
934  m_showMap->GetParent()->Layout();
935  }
936  else
937  {
938  // could not create preview, disable control and show error message
939  m_previewCtrl->Show(false);
940  m_labelControl->Show(true);
941  m_showMap->Show(false);
942  m_showMap->Enable(false);
943  m_showMap->GetParent()->Layout();
944  };
945  Update();
946 }
wxDEFINE_EVENT(EVT_QUEUE_PROGRESS, wxCommandEvent)
~BrowsePTOFilesDialog()
destructor, saves position
BrowsePTOFilesDialog(wxWindow *parent, const wxString startDirectory)
Constructor, read from xrc ressource; restore last uses settings and position.
WXIMPEX wxString GetFormattedTimeSpan(const wxTimeSpan &timeSpan)
Definition: wxutils.cpp:27
PanoramaOptions::ProjectionFormat getProjection() const
implementation of huginApp Class
generate thumbnail from given pto file
bool FileExists(const std::string &filename)
checks if file exists
Definition: utils.cpp:362
const float fieldOfView1
wxListCtrl * m_listCtrl
Definition: BrowseDialog.h:120
int SmallerComparisonOperator(const Type &a, const Type &b)
SrcPanoImage getSrcImage(unsigned imgNr) const
get a description of a source image
Definition: Panorama.cpp:1620
size_t nrActiveImages
Definition: BrowseDialog.h:53
#define THUMBNAIL_SIZE
#define HUGIN_CONV_FILENAME
Definition: platform.h:40
virtual ExitCode Entry()
unsigned int getHeight() const
get panorama height
some helper classes for graphes
int wxCALLBACK SortFieldOfViewDescending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
ThumbnailThread(BrowsePTOFilesDialog *parentDialog, const wxArrayString &fileList, const wxSize size)
void FillPanoInfo(const PanoInfo &info, long index)
add a new item to wxListCtrl and populate all columns
#define SORTASCENDING(functionName, var)
double focalLength35
Definition: BrowseDialog.h:60
void OnFileChanged(wxListEvent &e)
new file selected, generate preview for new file
friend class ThumbnailThread
Definition: BrowseDialog.h:138
wxString projectionName
Definition: BrowseDialog.h:55
double GPSLongitude
Definition: BrowseDialog.h:63
void OnThumbnailUpdate(wxCommandEvent &e)
for notifing from ThumbnailThread about new generated thumbnail generated thumbnail is transfered in ...
wxString lens
Definition: BrowseDialog.h:59
wxDateTime end
Definition: BrowseDialog.h:56
void GetMonitorProfile(wxString &profileName, cmsHPROFILE &profile)
Definition: wxcms.cpp:195
wxImage * GetwxImage() const
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition: huginApp.cpp:645
void OnDblClickListCtrl(wxMouseEvent &e)
double click does open file
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
wxSplitterWindow * m_splitter1
Definition: BrowseDialog.h:128
PanoInfo ParsePTOFile(const wxFileName file)
read the given pto file and add all information to wxListCtrl
options getSize().area()) int wxCALLBACK SortFieldOfViewAscending(wxIntPtr item1
Model for a panorama.
Definition: Panorama.h:152
std::string getPathPrefix(const std::string &filename)
Get the path to a filename.
Definition: utils.cpp:184
int wxCALLBACK SortDateAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
void OnListColClick(wxListEvent &e)
click on header to sort by column
void CorrectImage(wxImage &image, const vigra::ImageImportInfo::ICCProfile &iccProfile, const cmsHPROFILE &monitorProfile)
apply color correction to given image using input iccProfile and monitor profile
Definition: wxcms.cpp:218
wxString focalLengthString
Definition: BrowseDialog.h:61
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
static MainFrame * Get()
hack.. kind of a pseudo singleton...
Definition: MainFrame.cpp:2153
wxStaticText * m_labelControl
Definition: BrowseDialog.h:127
int wxCALLBACK SortDurationAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
wxString camera
Definition: BrowseDialog.h:58
#define SORTDESCENDING(functionName, var)
const int getExifDateTime(struct tm *datetime) const
try to convert Exif date time string to struct tm
Dialog for browsing pto files.
Definition: BrowseDialog.h:72
void StoreFramePosition(wxTopLevelWindow *frame, const wxString &basename)
Store window size and position in configfile/registry.
Definition: LensCalApp.cpp:210
wxGenericDirCtrl * m_dirCtrl
Definition: BrowseDialog.h:119
vigra::BRGBImage * m_preview
bool ReadPTOFile(const std::string &filename, const std::string &prefix="")
read pto file from the given filename into Panorama object it does some checks on the file and issues...
Definition: Panorama.cpp:2023
void OnDirectoryChanged(wxTreeEvent &e)
directory changed, load files from new directory
wxImageList m_thumbnails
image list with all thumbnails
Definition: BrowseDialog.h:133
helper class to transfer thumbnail data from worker thread to GUI thread
bool HasMonitorProfile() const
return true if we found a suitable monitor profile and could loading it
Definition: huginApp.h:134
wxDECLARE_EVENT(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE, wxCommandEvent)
HuginBase::PanoramaOptions options
Definition: BrowseDialog.h:54
void RestoreFramePosition(wxTopLevelWindow *frame, const wxString &basename)
Restore window size and position from configfile/registry.
Definition: LensCalApp.cpp:156
void GeneratePreview(int index)
generate preview for pto file with index
options wxIntPtr item2
wxArrayString m_files
wxSplitterWindow * m_splitter2
Definition: BrowseDialog.h:129
const float fieldOfView2
static double calcFocalLength(SrcPanoImage::Projection proj, double hfov, double crop, vigra::Size2D imageSize)
calcualte focal length, given crop factor and hfov
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
bool stringToDouble(const STR &str_, double &dest)
convert a string to a double, ignore localisation.
Definition: utils.h:114
std::vector< PanoInfo > m_ptoInfo
info about the pto file
Definition: BrowseDialog.h:131
void SortItems()
sort the items according to selected column
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
wxDateTime start
Definition: BrowseDialog.h:56
UIntSet getActiveImages() const
get active images
Definition: Panorama.cpp:1585
wxString GetSelectedProject()
return full path of selected project
wxImage * m_image
wxFileName ptoFilename
Definition: BrowseDialog.h:49
unsigned int getWidth() const
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
wxCriticalSection m_ThreadCS
critical section to synchronize with ThumbnailThread
Definition: BrowseDialog.h:137
options wxIntPtr wxIntPtr sortData std::vector< PanoInfo > * data
background thread to generate thumbnails of all pto files
wxStaticBitmap * m_previewCtrl
Definition: BrowseDialog.h:126
size_t nrImages
Definition: BrowseDialog.h:51
bool readEXIF()
try to fill out information about the image, by examining the exif data
bool GenerateThumbnail(const std::string pto_filename, const wxSize size, vigra::BRGBImage &panoImage, vigra::BImage &panoMask, vigra::ImageImportInfo::ICCProfile &iccProfile)
generate thumbnail image for given pto_filename
Definition of dialog to browse directory with pto files.
void OnListTypeChanged(wxCommandEvent &e)
Change display of wxListCtrl.
int wxCALLBACK SortDateDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
Definition: openmp_vigra.h:305
BrowsePTOFilesDialog * m_parentDialog
static void info(const char *fmt,...)
Definition: svm.cpp:95
class to store some information about a pto file on disc
Definition: BrowseDialog.h:46
double GPSLatitude
Definition: BrowseDialog.h:64
void OnOk(wxCommandEvent &e)
Saves current expression when closing dialog with Ok.
wxString GetFocalLength(const HuginBase::SrcPanoImage *img)
return focallength and focallength 35 mm as wxString
Definition: LensTools.cpp:519
ThreadImage(vigra::BRGBImage *thumbnail, vigra::BImage *mask)
ThreadImage()=delete
int GreaterComparisonOperator(const Type &a, const Type &b)
All variables of a source image.
Definition: SrcPanoImage.h:194
ThumbnailThread * m_thumbnailThread
background thumbnail creater thread
Definition: BrowseDialog.h:135
long TranslateIndex(const long index)
translate the index to the index of m_ptoInfo
wxTimeSpan duration
Definition: BrowseDialog.h:57
int wxCALLBACK SortDurationDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
void OnShowOnMap(wxCommandEvent &e)
show current pano on Openstreetmap
void EndThumbnailThread()
end background thumbnail creating thread
void UpdateItemTexts(long newStyle)
update all item texts
vigra::BImage * m_mask
wxString GetSelectedPath()
return last selected path