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