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 #ifdef __WXMSW__
161  wxIconBundle myIcons(huginApp::Get()->GetXRCPath() + wxT("data/hugin.ico"),wxBITMAP_TYPE_ICO);
162  SetIcons(myIcons);
163 #else
164  wxIcon myIcon(huginApp::Get()->GetXRCPath() + wxT("data/hugin.png"),wxBITMAP_TYPE_PNG);
165  SetIcon(myIcon);
166 #endif
167  m_dirCtrl = XRCCTRL(*this, "browse_dirctrl", wxGenericDirCtrl);
168  m_dirCtrl->Bind(wxEVT_DIRCTRL_SELECTIONCHANGED, &BrowsePTOFilesDialog::OnDirectoryChanged, this);
169  m_listCtrl = XRCCTRL(*this, "browse_listctrl", wxListCtrl);
170  m_listCtrl->Bind(wxEVT_LIST_ITEM_SELECTED, &BrowsePTOFilesDialog::OnFileChanged, this);
171  m_listCtrl->Bind(wxEVT_LIST_COL_CLICK, &BrowsePTOFilesDialog::OnListColClick, this);
172  m_previewCtrl = XRCCTRL(*this, "browse_preview", wxStaticBitmap);
173  m_splitter1 = XRCCTRL(*this, "browse_splitter1", wxSplitterWindow);
174  m_splitter2 = XRCCTRL(*this, "browse_splitter2", wxSplitterWindow);
175  m_showMap = XRCCTRL(*this, "browse_show_map", wxButton);
176  m_labelControl = XRCCTRL(*this, "browse_statictext", wxStaticText);
177  m_labelControl->SetFont(m_labelControl->GetFont().Larger().Larger());
178  m_thumbnails.Create(THUMBNAIL_SIZE, THUMBNAIL_SIZE, true, 0);
179  m_listCtrl->SetWindowStyle(wxLC_REPORT | wxLC_AUTOARRANGE | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES);
180  m_listCtrl->SetImageList(&m_thumbnails, wxIMAGE_LIST_NORMAL);
181  m_listCtrl->Bind(wxEVT_LEFT_DCLICK, &BrowsePTOFilesDialog::OnDblClickListCtrl, this);
182  m_listCtrl->InsertColumn(0, _("Filename"), wxLIST_FORMAT_LEFT, 300);
183  m_listCtrl->InsertColumn(1, _("# images"), wxLIST_FORMAT_RIGHT, 50);
184  m_listCtrl->InsertColumn(2, _("# active images"), wxLIST_FORMAT_RIGHT, 50);
185  m_listCtrl->InsertColumn(3, _("Projection"), wxLIST_FORMAT_LEFT, 200);
186  m_listCtrl->InsertColumn(4, _("Field of view"), wxLIST_FORMAT_LEFT, 100);
187  m_listCtrl->InsertColumn(5, _("Canvas size"), wxLIST_FORMAT_LEFT, 100);
188  m_listCtrl->InsertColumn(6, _("Model"), wxLIST_FORMAT_LEFT, 200);
189  m_listCtrl->InsertColumn(7, _("Lens"), wxLIST_FORMAT_LEFT, 250);
190  m_listCtrl->InsertColumn(8, _("Focal length"), wxLIST_FORMAT_LEFT, 150);
191  m_listCtrl->InsertColumn(9, _("Capture date"), wxLIST_FORMAT_LEFT, 150);
192  m_listCtrl->InsertColumn(10, _("Duration"), wxLIST_FORMAT_LEFT, 50);
193  m_listType = XRCCTRL(*this, "browse_list_type", wxChoice);
194  m_listType->Bind(wxEVT_CHOICE, &BrowsePTOFilesDialog::OnListTypeChanged, this);
195  // restore some settings
196  RestoreFramePosition(this, "BrowsePTODialog");
197  wxConfigBase* config = wxConfigBase::Get();
198  //splitter position
199  int splitter_pos = config->Read("/BrowsePTODialog/splitterPos1", -1l);
200  if (splitter_pos > 0)
201  {
202  m_splitter1->SetSashPosition(splitter_pos);
203  };
204  splitter_pos = config->Read("/BrowsePTODialog/splitterPos2", -1l);
205  if (splitter_pos > 0)
206  {
207  m_splitter2->SetSashPosition(splitter_pos);
208  };
209  //get saved width
210  for (int j = 0; j < m_listCtrl->GetColumnCount(); j++)
211  {
212  // -1 is auto
213  int width = config->Read(wxString::Format("/BrowsePTODialog/ColumnWidth%d", j), -1);
214  if (width != -1 && width > 5)
215  {
216  m_listCtrl->SetColumnWidth(j, width);
217  };
218  };
219  m_sortCol = config->Read("/BrowsePTODialog/SortColumn", -1);
220  m_sortAscending = config->Read("/BrowsePTODialog/SortAscending", 1) == 1 ? true : false;
221  if (m_sortCol != -1)
222  {
223  m_listCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
224  };
225 
226  // fill values for start directory
227  if (!startDirectory.IsEmpty())
228  {
229  m_dirCtrl->SetPath(startDirectory);
230  };
231  long listType = config->Read("/BrowsePTODialog/ListType", 0l);
232  m_listType->SetSelection(listType);
233  wxCommandEvent event;
234  event.SetInt(listType);
235  OnListTypeChanged(event);
236  Bind(wxEVT_BUTTON, &BrowsePTOFilesDialog::OnOk, this, wxID_OK);
237  Bind(wxEVT_BUTTON, &BrowsePTOFilesDialog::OnShowOnMap, this, XRCID("browse_show_map"));
238  Bind(wxEVT_COMMAND_THUMBNAILTHREAD_UPDATE, &BrowsePTOFilesDialog::OnThumbnailUpdate, this);
239 };
240 
242 {
243  // stop working thread
245  // save some settings
246  StoreFramePosition(this, "BrowsePTODialog");
247  wxConfigBase* config = wxConfigBase::Get();
248  config->Write("/BrowsePTODialog/splitterPos1", m_splitter1->GetSashPosition());
249  config->Write("/BrowsePTODialog/splitterPos2", m_splitter2->GetSashPosition());
250  // save width of all columns in wxListCtrl
251  for (int j = 0; j < m_listCtrl->GetColumnCount(); j++)
252  {
253  config->Write(wxString::Format("/BrowsePTODialog/ColumnWidth%d", j), m_listCtrl->GetColumnWidth(j));
254  };
255  config->Write("/BrowsePTODialog/ListType", m_listType->GetSelection());
256  config->Write(wxT("/BrowsePTODialog/SortColumn"), m_sortCol);
257  config->Write(wxT("/BrowsePTODialog/SortAscending"), m_sortAscending ? 1 : 0);
258  config->Flush();
259 }
260 
262 {
263  long index = -1;
264  if (m_listCtrl->GetSelectedItemCount() == 1)
265  {
266  index = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
267  };
268  if (index >= 0 && index < m_ptoInfo.size())
269  {
270  return m_ptoInfo[TranslateIndex(index)].ptoFilename.GetFullPath();
271  }
272  else
273  {
274  return wxEmptyString;
275  };
276 }
277 
279 {
280  return m_dirCtrl->GetPath();
281 }
282 
283 void BrowsePTOFilesDialog::OnOk(wxCommandEvent& e)
284 {
285  if (m_listCtrl->GetSelectedItemCount() == 1)
286  {
287  EndModal(wxID_OK);
288  }
289  else
290  {
291  wxBell();
292  }
293 }
294 
296 {
297  PanoInfo info;
298  info.ptoFilename = file;
299  // read pto file
300  HuginBase::Panorama pano;
301  const std::string input(file.GetFullPath().mb_str(HUGIN_CONV_FILENAME));
302  if (!pano.ReadPTOFile(input, hugin_utils::getPathPrefix(input)))
303  {
304  return info;
305  };
306  // fill class to store some information about the pano
307  info.nrImages = pano.getNrOfImages();
308  info.nrActiveImages = pano.getActiveImages().size();
309  info.options = pano.getOptions();
310  // translate projection name
311  {
312  pano_projection_features proj;
313  wxString s;
314  if (panoProjectionFeaturesQuery(info.options.getProjection(), &proj))
315  {
316  wxString str2(proj.name, wxConvLocal);
317  info.projectionName = wxGetTranslation(str2);
318  };
319  };
320  // read some EXIF data
321  const HuginBase::UIntSet activeImages = pano.getActiveImages();
322  if (!activeImages.empty())
323  {
324  HuginBase::SrcPanoImage img0 = pano.getSrcImage(*activeImages.begin());
325  if(!hugin_utils::FileExists(img0.getFilename()))
326  {
327  return info;
328  }
329  img0.readEXIF();
330  info.camera = img0.getExifModel();
331  info.lens = img0.getExifLens();
332  info.focalLength35 = img0.getExifFocalLength35();
333  if (info.focalLength35 < 0.01 && img0.getCropFactor()>0)
334  {
335  info.focalLength35 = img0.getExifFocalLength() * img0.getCropFactor();
336  };
337  if (info.focalLength35 < 0.01)
338  {
339  info.focalLength35 = HuginBase::SrcPanoImage::calcFocalLength(img0.getProjection(), img0.getHFOV(), 1.0, img0.getSize());
340  };
342  // read date/time of all active images
343  if (!img0.getExifDate().empty())
344  {
345  struct tm exifdatetime;
346  if (img0.getExifDateTime(&exifdatetime) == 0)
347  {
348  info.start = wxDateTime(exifdatetime);
349  info.end = info.start;
350  };
351  // iterate all images, except the first one, this image is already handled before
352  HuginBase::UIntSet imgs(activeImages);
353  imgs.erase(imgs.begin());
354  for (const auto& img : imgs)
355  {
356  HuginBase::SrcPanoImage imgSrcImage = pano.getSrcImage(img);
357  if (!hugin_utils::FileExists(imgSrcImage.getFilename()))
358  {
359  continue;
360  };
361  imgSrcImage.readEXIF();
362  memset(&exifdatetime, 0, sizeof(exifdatetime));
363  if (!imgSrcImage.getExifDate().empty() && imgSrcImage.getExifDateTime(&exifdatetime) == 0)
364  {
365  const wxDateTime dateTime = wxDateTime(exifdatetime);
366  if (info.start.IsValid())
367  {
368  if (dateTime.IsEarlierThan(info.start))
369  {
370  info.start = dateTime;
371  }
372  if (dateTime.IsLaterThan(info.end))
373  {
374  info.end = dateTime;
375  }
376  }
377  else
378  {
379  info.start = dateTime;
380  info.end = dateTime;
381  }
382  }
383  }
384  if (info.start.IsValid())
385  {
386  info.duration = info.end.Subtract(info.start);
387  }
388  };
389  const auto& fileMetadata = img0.getFileMetadata();
390  const auto& latitude = fileMetadata.find("latitude");
391  const auto& longitude = fileMetadata.find("longitude");
392  if (latitude != fileMetadata.end())
393  {
394  hugin_utils::stringToDouble(latitude->second, info.GPSLatitude);
395  };
396  if (longitude != fileMetadata.end())
397  {
398  hugin_utils::stringToDouble(longitude->second, info.GPSLongitude);
399  };
400  };
401  return info;
402 }
403 
405 {
406 #ifndef __WXMSW__
407  // in generic implementation we can add text to column only in report view
408  if (m_listCtrl->InReportView())
409 #endif
410  {
411  m_listCtrl->SetItem(index, 1, wxString::Format("%zu", info.nrImages));
412  m_listCtrl->SetItem(index, 2, wxString::Format("%zu", info.nrActiveImages));
413  m_listCtrl->SetItem(index, 3, info.projectionName);
414  m_listCtrl->SetItem(index, 4, wxString::Format("%.0f x %.0f", info.options.getHFOV(), info.options.getVFOV()));
415  m_listCtrl->SetItem(index, 5, wxString::Format("%u x %u", info.options.getWidth(), info.options.getHeight()));
416  m_listCtrl->SetItem(index, 6, info.camera);
417  m_listCtrl->SetItem(index, 7, info.lens);
418  m_listCtrl->SetItem(index, 8, info.focalLengthString);
419  if (info.start.IsValid())
420  {
421  m_listCtrl->SetItem(index, 9, info.start.Format());
422  };
423  m_listCtrl->SetItem(index, 10, GetFormattedTimeSpan(info.duration));
424  };
425 }
426 
428 {
429 #ifndef __WXMSW__
430  m_listCtrl->DeleteAllItems();
431 #endif
432  m_listCtrl->SetWindowStyle(newStyle);
433 #ifndef __WXMSW__
434  // changing the window style does invalidate the items, so we delete all items
435  // and add then again
436  for (size_t i = 0; i < m_ptoInfo.size(); ++i)
437  {
438  m_listCtrl->InsertItem(i, m_ptoInfo[i].ptoFilename.GetFullName(), -1);
439  m_listCtrl->SetItemData(i, i);
440  FillPanoInfo(m_ptoInfo[i], i);
441  };
442  SortItems();
443 #endif
444 }
445 
447 {
448  if (m_dirCtrl && m_listCtrl)
449  {
450  wxWindowDisabler disableAll;
451  wxBusyInfo busy(wxBusyInfoFlags().Icon(MainFrame::Get()->GetIcon()).Label(wxString::Format(_("Reading directory %s"), m_dirCtrl->GetPath().c_str())));
452  // stop running thumbnail thread from last directory if not finished yet
454  // find all pto files in directory
455  m_ptoInfo.clear();
456  m_thumbnails.RemoveAll();
457  SetTitle(wxString::Format(_("Browse project files in %s"), m_dirCtrl->GetPath().c_str()));
458  wxArrayString files;
459  wxDir::GetAllFiles(m_dirCtrl->GetPath(), &files, "*.pto", wxDIR_FILES | wxDIR_HIDDEN | wxDIR_NO_FOLLOW);
460  // add to wxListCtrl
461  m_listCtrl->DeleteAllItems();
462  m_ptoInfo.resize(files.size());
463  for (size_t i = 0; i < files.size(); ++i)
464  {
465  const wxFileName filename(files[i]);
466  m_ptoInfo[i] = ParsePTOFile(filename);
467  m_listCtrl->InsertItem(i, m_ptoInfo[i].ptoFilename.GetFullName(), -1);
468  m_listCtrl->SetItemData(i, i);
469  FillPanoInfo(m_ptoInfo[i], i);
470  };
471  SortItems();
472  // start background thread for creating all thumbnails
473  m_thumbnailThread = new ThumbnailThread(this, files, m_thumbnails.GetSize());
474  if (!m_listCtrl->InReportView())
475  {
476  m_thumbnailThread->Run();
477  };
478  // clear preview area
479  m_previewCtrl->SetBitmap(wxBitmap());
480  m_previewCtrl->Show(true);
481  m_labelControl->Show(false);
482  m_showMap->Show(false);
483  m_showMap->Enable(false);
484  m_showMap->GetParent()->Layout();
485  };
486 }
487 
489 {
490  if (e.GetIndex() >= 0)
491  {
492  GeneratePreview(TranslateIndex(e.GetIndex()));
493  };
494 }
495 
497 {
498  if (m_listCtrl->GetSelectedItemCount() == 1)
499  {
500  EndModal(wxID_OK);
501  };
502 }
503 
505 {
506  const int newCol = e.GetColumn();
507  if (m_sortCol == newCol)
508  {
510  }
511  else
512  {
513  m_sortCol = newCol;
514  m_sortAscending = true;
515  };
516  m_listCtrl->ShowSortIndicator(m_sortCol, m_sortAscending);
517  SortItems();
518  Refresh();
519 }
520 
522 {
523  // we have created a new thumbnail, make the wxListCtrl aware of it
524  const int index = e.GetInt();
525  ThreadImage* thumbnail= wxDynamicCast(e.GetEventObject(), ThreadImage);
526  m_ptoInfo[index].imageIndex = m_thumbnails.Add(*(thumbnail->GetwxImage()));
527  for (size_t i = 0; i < m_listCtrl->GetItemCount(); ++i)
528  {
529  if (m_listCtrl->GetItemData(i) == index)
530  {
531  m_listCtrl->SetItemImage(i, m_ptoInfo[index].imageIndex);
532  break;
533  };
534  };
535  delete thumbnail;
536 #ifndef __WXMSW__
537  // a simple Refresh for repainting the control is not working
538  // all thumbnails are drawn on top of each other
539  // only when all done after a size event all is drawn correctly
540  m_listCtrl->SendSizeEvent();
541 #endif
542 }
543 
545 {
546  if (e.GetSelection() == 0)
547  {
548  UpdateItemTexts(wxLC_ICON | wxLC_AUTOARRANGE | wxLC_SINGLE_SEL);
549  // start creating thumbnail images in background thread
550  if (m_thumbnailThread && !m_thumbnailThread->IsRunning())
551  {
552  m_thumbnailThread->Run();
553  };
554  }
555  else
556  {
557  UpdateItemTexts(wxLC_REPORT | wxLC_AUTOARRANGE | wxLC_SINGLE_SEL | wxLC_HRULES | wxLC_VRULES);
558  };
559 #ifndef __WXMSW__
560  // a simple Refresh for repainting the control is not working
561  // all thumbnails are drawn on top of each other
562  // only when all done after a size event all is drawn correctly
563  m_listCtrl->SendSizeEvent();
564 #endif
565 }
566 
567 void BrowsePTOFilesDialog::OnShowOnMap(wxCommandEvent& e)
568 {
569  long index = -1;
570  if (m_listCtrl->GetSelectedItemCount() == 1)
571  {
572  index = m_listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
573  };
574  if (index >= 0 && index < m_ptoInfo.size())
575  {
576  if (m_ptoInfo[index].HasGPS())
577  {
578  const wxString openstreetMapLink = "https://www.openstreetmap.org/?mlat=" + wxString::FromCDouble(m_ptoInfo[index].GPSLatitude) + "&mlon=" + wxString::FromCDouble(m_ptoInfo[index].GPSLongitude);
579  wxLaunchDefaultBrowser(openstreetMapLink);
580  }
581  }
582 }
583 
585 {
586  return m_listCtrl->GetItemData(index);
587 }
588 
589 template <class Type>
590 int GreaterComparisonOperator(const Type& a, const Type& b)
591 {
592  if (a > b)
593  {
594  return 1;
595  }
596  else
597  {
598  if (a < b)
599  {
600  return -1;
601  }
602  else {
603  return 0;
604  };
605  }
606 }
607 
608 template <class Type>
609 int SmallerComparisonOperator(const Type& a, const Type& b)
610 {
611  if (a < b)
612  {
613  return 1;
614  }
615  else
616  {
617  if (a > b)
618  {
619  return -1;
620  }
621  else {
622  return 0;
623  };
624  }
625 }
626 
627 #define SORTASCENDING(functionName, var) \
628 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
629 {\
630  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
631  return GreaterComparisonOperator(data->at(item1).var, data->at(item2).var);\
632 }
633 #define SORTDESCENDING(functionName, var) \
634 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
635 {\
636  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
637  return SmallerComparisonOperator(data->at(item1).var, data->at(item2).var);\
638 }
639 
640 SORTASCENDING(SortFilenameAscending, ptoFilename.GetFullName())
641 SORTDESCENDING(SortFilenameDescending, ptoFilename.GetFullName())
642 SORTASCENDING(SortNrImagesAscending, nrImages)
643 SORTDESCENDING(SortNrImagesDescending, nrImages)
644 SORTASCENDING(SortNrActiveImagesAscending, nrActiveImages)
645 SORTDESCENDING(SortNrActiveImagesDescending, nrActiveImages)
646 SORTASCENDING(SortProjectionAscending, projectionName)
647 SORTDESCENDING(SortProjectionDescending, projectionName)
648 SORTASCENDING(SortCanvasSizeAcending, options.getSize().area())
649 SORTDESCENDING(SortCanvasSizeDescending, options.getSize().area())
650 SORTASCENDING(SortFocalLengthAscending, focalLength35)
651 SORTDESCENDING(SortFocalLengthDescending, focalLength35)
652 
653 #undef SORTASCENDING
654 #undef SORTDESCENDING
655 
656 // special variant for wxStrings
657 #define SORTASCENDING(functionName, var) \
658 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
659 {\
660  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
661  return data->at(item1).var.CmpNoCase(data->at(item2).var);\
662 }
663 #define SORTDESCENDING(functionName, var) \
664 int wxCALLBACK functionName(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)\
665 {\
666  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);\
667  return -(data->at(item1).var.CmpNoCase(data->at(item2).var));\
668 }
669 SORTASCENDING(SortCameraAscending, camera)
670 SORTDESCENDING(SortCameraDescending, camera)
671 SORTASCENDING(SortLensAscending, lens)
672 SORTDESCENDING(SortLensDescending, lens)
673 
674 #undef SORTASCENDING
675 #undef SORTDESCENDING
676 
677 // sort by field of view
678 int wxCALLBACK SortFieldOfViewAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
679 {
680  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
681  const float fieldOfView1 = data->at(item1).options.getHFOV() * 1000 + data->at(item1).options.getVFOV();
682  const float fieldOfView2 = data->at(item2).options.getHFOV() * 1000 + data->at(item2).options.getVFOV();
683  return GreaterComparisonOperator(fieldOfView1, fieldOfView2);
684 }
685 
686 int wxCALLBACK SortFieldOfViewDescending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
687 {
688  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
689  const float fieldOfView1 = data->at(item1).options.getHFOV() * 1000 + data->at(item1).options.getVFOV();
690  const float fieldOfView2 = data->at(item2).options.getHFOV() * 1000 + data->at(item2).options.getVFOV();
691  return SmallerComparisonOperator(fieldOfView1, fieldOfView2);
692 }
693 
694 // sort by date
695 int wxCALLBACK SortDateAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
696 {
697  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
698  if (data->at(item1).start.IsLaterThan(data->at(item2).start))
699  {
700  return 1;
701  }
702  if (data->at(item1).start.IsEarlierThan(data->at(item2).start))
703  {
704  return -1;
705  }
706  return 0;
707 };
708 
709 int wxCALLBACK SortDateDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
710 {
711  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
712  if (data->at(item1).start.IsEarlierThan(data->at(item2).start))
713  {
714  return 1;
715  }
716  if (data->at(item1).start.IsLaterThan(data->at(item2).start))
717  {
718  return -1;
719  }
720  return 0;
721 };
722 
723 // sort by duration
724 int wxCALLBACK SortDurationAscending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
725 {
726  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
727  if (data->at(item1).duration.IsLongerThan(data->at(item2).duration))
728  {
729  return 1;
730  }
731  if (data->at(item1).duration.IsShorterThan(data->at(item2).duration))
732  {
733  return -1;
734  }
735  return 0;
736 };
737 
738 int wxCALLBACK SortDurationDesending(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData)
739 {
740  std::vector<PanoInfo>* data = (std::vector<PanoInfo>*)(sortData);
741  if (data->at(item1).duration.IsShorterThan(data->at(item2).duration))
742  {
743  return 1;
744  }
745  if (data->at(item1).duration.IsLongerThan(data->at(item2).duration))
746  {
747  return -1;
748  }
749  return 0;
750 };
751 
753 {
754  if (m_sortCol > -1)
755  {
756  switch (m_sortCol)
757  {
758  default:
759  case 0: // filename
760  if (m_sortAscending)
761  {
762  m_listCtrl->SortItems(SortFilenameAscending, wxIntPtr(&m_ptoInfo));
763  }
764  else
765  {
766  m_listCtrl->SortItems(SortFilenameDescending, wxIntPtr(&m_ptoInfo));
767  };
768  break;
769  case 1: // number of images
770  if (m_sortAscending)
771  {
772  m_listCtrl->SortItems(SortNrImagesAscending, wxIntPtr(&m_ptoInfo));
773  }
774  else
775  {
776  m_listCtrl->SortItems(SortNrImagesDescending, wxIntPtr(&m_ptoInfo));
777  };
778  break;
779  case 2: // number of active images
780  if (m_sortAscending)
781  {
782  m_listCtrl->SortItems(SortNrActiveImagesAscending, wxIntPtr(&m_ptoInfo));
783  }
784  else
785  {
786  m_listCtrl->SortItems(SortNrActiveImagesDescending, wxIntPtr(&m_ptoInfo));
787  };
788  break;
789  case 3: // projection
790  if (m_sortAscending)
791  {
792  m_listCtrl->SortItems(SortProjectionAscending, wxIntPtr(&m_ptoInfo));
793  }
794  else
795  {
796  m_listCtrl->SortItems(SortProjectionDescending, wxIntPtr(&m_ptoInfo));
797  };
798  break;
799  case 4: // field of view
800  if (m_sortAscending)
801  {
802  m_listCtrl->SortItems(SortFieldOfViewAscending, wxIntPtr(&m_ptoInfo));
803  }
804  else
805  {
806  m_listCtrl->SortItems(SortFieldOfViewDescending, wxIntPtr(&m_ptoInfo));
807  };
808  break;
809  case 5: // canvas size
810  if (m_sortAscending)
811  {
812  m_listCtrl->SortItems(SortCanvasSizeAcending, wxIntPtr(&m_ptoInfo));
813  }
814  else
815  {
816  m_listCtrl->SortItems(SortCanvasSizeDescending, wxIntPtr(&m_ptoInfo));
817  };
818  break;
819  case 6: // model
820  if (m_sortAscending)
821  {
822  m_listCtrl->SortItems(SortCameraAscending, wxIntPtr(&m_ptoInfo));
823  }
824  else
825  {
826  m_listCtrl->SortItems(SortCameraDescending, wxIntPtr(&m_ptoInfo));
827  };
828  break;
829  case 7: // lens
830  if (m_sortAscending)
831  {
832  m_listCtrl->SortItems(SortLensAscending, wxIntPtr(&m_ptoInfo));
833  }
834  else
835  {
836  m_listCtrl->SortItems(SortLensDescending, wxIntPtr(&m_ptoInfo));
837  };
838  break;
839  case 8: // focal length
840  if (m_sortAscending)
841  {
842  m_listCtrl->SortItems(SortFocalLengthAscending, wxIntPtr(&m_ptoInfo));
843  }
844  else
845  {
846  m_listCtrl->SortItems(SortFocalLengthDescending, wxIntPtr(&m_ptoInfo));
847  };
848  break;
849  case 9: // capture date
850  if (m_sortAscending)
851  {
852  m_listCtrl->SortItems(SortDateAscending, wxIntPtr(&m_ptoInfo));
853  }
854  else
855  {
856  m_listCtrl->SortItems(SortDateDesending, wxIntPtr(&m_ptoInfo));
857  };
858  break;
859  case 10: // duration
860  if (m_sortAscending)
861  {
862  m_listCtrl->SortItems(SortDurationAscending, wxIntPtr(&m_ptoInfo));
863  }
864  else
865  {
866  m_listCtrl->SortItems(SortDurationDesending, wxIntPtr(&m_ptoInfo));
867  };
868  break;
869  };
870  };
871 };
872 
874 {
875  // delete running background thread
876  wxBusyCursor busy;
877  {
878  bool deleteStoppedThread = false;
879  {
880  wxCriticalSectionLocker enter(m_ThreadCS);
881  if (m_thumbnailThread)
882  {
883  if (m_thumbnailThread->IsRunning())
884  {
885  m_thumbnailThread->Delete();
886  }
887  else
888  {
889  deleteStoppedThread = true;
890  };
891  };
892  };
893  if (deleteStoppedThread)
894  {
895  delete m_thumbnailThread;
896  };
897  };
898  // exit from the critical section to give the thread
899  // the possibility to enter its destructor
900  // (which is guarded with m_ThreadCS critical section!)
901  while (1)
902  {
903  {
904  wxCriticalSectionLocker enter(m_ThreadCS);
905  if (!m_thumbnailThread)
906  {
907  break;
908  };
909  };
910  // wait for thread completion
911  wxThread::This()->Sleep(1);
912  };
913 }
914 
916 {
917  if (index<0 || index>m_ptoInfo.size())
918  {
919  return;
920  };
921  wxWindowDisabler disableAll;
922  wxBusyInfo busyInfo(wxBusyInfoFlags().Icon(MainFrame::Get()->GetIcon()).Label(wxString::Format(_("Generating preview for %s"), m_ptoInfo[index].ptoFilename.GetFullName().c_str())));
923  wxSize previewSize = m_previewCtrl->GetParent()->GetSize();
924  vigra::BRGBImage preview;
925  vigra::BImage mask;
926  vigra::ImageImportInfo::ICCProfile iccProfile;
927  if (GenerateThumbnail(m_ptoInfo[index].ptoFilename.GetFullPath().ToStdString(), previewSize, preview, mask, iccProfile))
928  {
929  wxImage image(preview.width(), preview.height(), (unsigned char*)preview.data(), (unsigned char*)mask.data(), true);
930  // now apply color profile
931  if (!iccProfile.empty() || huginApp::Get()->HasMonitorProfile())
932  {
934  };
935  // now show in GUI
936  m_previewCtrl->SetBitmap(image);
937  m_previewCtrl->Show(true);
938  m_labelControl->Show(false);
939  m_showMap->Show(m_ptoInfo[index].HasGPS());
940  m_showMap->Enable(m_ptoInfo[index].HasGPS());
941  m_showMap->GetParent()->Layout();
942  }
943  else
944  {
945  // could not create preview, disable control and show error message
946  m_previewCtrl->Show(false);
947  m_labelControl->Show(true);
948  m_showMap->Show(false);
949  m_showMap->Enable(false);
950  m_showMap->GetParent()->Layout();
951  };
952  Update();
953 }
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:641
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:2169
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:212
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:152
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:158
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