Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EditOutputIniDialog.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 "EditOutputIniDialog.h"
28 #include "hugin_config.h"
29 #include "panoinc_WX.h"
30 #include "hugin/huginApp.h"
31 #include <wx/fileconf.h>
32 #include <wx/wfstream.h>
33 #include <wx/sstream.h>
34 #include <wx/propgrid/advprops.h>
35 
36 static wxArrayString BlenderArray;
37 static wxArrayString ProjectionArray;
38 static wxArrayString OutputTypeArray;
39 static wxArrayString LDRFileTypeArray;
40 static wxArrayString HDRFileTypeArray;
41 
42 void InitArrays()
43 {
44  BlenderArray.Add("Enblend");
45  BlenderArray.Add("Internal");
46 
47  int nP = panoProjectionFormatCount();
48  for (int n = 0; n < nP; n++)
49  {
50  pano_projection_features proj;
51  if (panoProjectionFeaturesQuery(n, &proj))
52  {
53  ProjectionArray.Add(wxString(proj.name, wxConvLocal));
54  };
55  };
56 
57  OutputTypeArray.Add("Normal");
58  OutputTypeArray.Add("StacksFusedBlended");
59  OutputTypeArray.Add("ExposureLayersFused");
60  OutputTypeArray.Add("HDR");
61  OutputTypeArray.Add("Remap");
62  OutputTypeArray.Add("RemapOrig");
63  OutputTypeArray.Add("HDRRemap");
64  OutputTypeArray.Add("FusedStacks");
65  OutputTypeArray.Add("HDRStacks");
66  OutputTypeArray.Add("ExposureLayers");
67 
68  LDRFileTypeArray.Add("jpg");
69  LDRFileTypeArray.Add("tif");
70  LDRFileTypeArray.Add("png");
71 
72  HDRFileTypeArray.Add("exr");
73  HDRFileTypeArray.Add("tif");
74 }
75 
77 static const wxString defaultIni
78 {
79  "[Cylindrical]\n"
80  "Canvas=70%\n"
81  "Condition1=ImageCount>1\n"
82  "Condition2=PanoVFOV<100\n"
83  "Condition3=PanoHFOV>=100\n"
84  "Crop=auto\n"
85  "FOV=auto\n"
86  "Projection=Cylindrical\n"
87  "[Equirectangular]\n"
88  "Canvas=70%\n"
89  "Condition1=ImageCount>1\n"
90  "Condition2=PanoVFOV>=100\n"
91  "Crop=auto\n"
92  "FOV=auto\n"
93  "Projection=Equirectangular\n"
94  "[Rectilinear]\n"
95  "Canvas=70%\n"
96  "Condition1=ImageCount>1\n"
97  "Condition2=PanoHFOV<100\n"
98  "Condition3=PanoVFOV<100\n"
99  "Crop=auto\n"
100  "FOV=auto\n"
101  "Projection=Rectilinear"
102 };
103 
105 bool contains(const wxArrayString& stringArray, const wxString& string, bool caseInSensitive = true)
106 {
107  if (caseInSensitive)
108  {
109  for (size_t i = 0; i < stringArray.GetCount(); ++i)
110  {
111  if (stringArray[i].CmpNoCase(string) == 0)
112  {
113  return true;
114  };
115  };
116  }
117  else
118  {
119  for (size_t i = 0; i < stringArray.GetCount(); ++i)
120  {
121  if (stringArray[i].Cmp(string) == 0)
122  {
123  return true;
124  };
125  };
126  };
127  return false;
128 }
129 
130 EditOutputIniDialog::EditOutputIniDialog(wxWindow* parent) :wxDialog(parent, wxID_ANY, _("Edit assistant output settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
131 {
132 #ifdef __WXMSW__
133  wxIconBundle myIcons(huginApp::Get()->GetXRCPath() + wxT("data/hugin.ico"), wxBITMAP_TYPE_ICO);
134  SetIcons(myIcons);
135 #else
136  wxIcon myIcon(huginApp::Get()->GetXRCPath() + wxT("data/hugin.png"), wxBITMAP_TYPE_PNG);
137  // set the icon in the title bar
138  SetIcon(myIcon);
139 #endif
140 
141  wxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
142  m_grid = new wxPropertyGridManager(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_AUTO_SORT | wxPG_DESCRIPTION | wxPG_SPLITTER_AUTO_CENTER);
143  // bind context menu only to the grid
144  // when binding to whole control it would also be invoked above the text area below
145  m_grid->GetGrid()->Bind(wxEVT_CONTEXT_MENU, &EditOutputIniDialog::OnContextMenu, this);
146  topSizer->Add(m_grid, wxSizerFlags(1).Expand().Border(wxALL, 10));
147  wxStdDialogButtonSizer* buttonSizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
148  buttonSizer->GetAffirmativeButton()->Bind(wxEVT_BUTTON, &EditOutputIniDialog::OnOk, this);
149  topSizer->Add(buttonSizer, wxSizerFlags().Expand().Border(wxALL, 6));
150  SetSizerAndFit(topSizer);
151  InitArrays();
152  ReadIni();
153  RestoreFramePosition(this, "EditOutputIniDialog");
154 }
155 
157 {
158  StoreFramePosition(this, "EditOutputIniDialog");
159 }
160 
161 void EditOutputIniDialog::OnOk(wxCommandEvent& e)
162 {
163  WriteIni();
164  EndModal(wxOK);
165 }
166 
167 void EditOutputIniDialog::AddKey(wxPGProperty* section, const wxString& key, const wxString& value)
168 {
169  if (key.SubString(0, 8).CmpNoCase("Condition") == 0)
170  {
171  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
172  p->SetHelpString(_("Define which condition should be fulfilled to apply this setting.\nThe variables CameraModel, CameraMaker, Lens, HFOV, LensType, Focallength, Focallength35mm, Filename, Path, ImageCount, PanoHFOV and PanoVFOV can be used.\nFor comparison you can use ==, !=, =~ and !~ for strings and ==, !=, <, <=, > and >= for numbers."));
173  return;
174  };
175  if (key.CmpNoCase("Projection") == 0)
176  {
177  wxPGProperty* p = m_grid->AppendIn(section, new wxEnumProperty(key, wxPG_LABEL, ProjectionArray));
178  if (value.IsEmpty())
179  {
180  p->SetValue(ProjectionArray[0]);
181  }
182  else
183  {
184  p->SetValue(value);
185  };
186  p->SetHelpString(_("Select which projection should be selected."));
187  return;
188  };
189  if (key.CmpNoCase("FOV") == 0)
190  {
191  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
192  p->SetHelpString(_("Select which field of view should be set.\nCan be Auto for automatic detection, or a fixed horizontal field of view (e.g. 120) or horizontal x vertical field of view (e.g. 140x70)."));
193  return;
194  };
195  if (key.CmpNoCase("Canvas") == 0)
196  {
197  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
198  p->SetHelpString(_("Select to which size the canvas should be set.\nFor automatic detection use Auto or a scale factor in percent (e.g. 70%).\nOr give a width x height (e.g. 8000x4000)."));
199  return;
200  };
201  if (key.CmpNoCase("Crop") == 0)
202  {
203  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
204  p->SetHelpString(_("Select which crop should be used.\nFor automatic detection use Auto, AutoOutside or AutoHDR.\nOr give 4 numbers as left, right, top and bottom. You can use absolute pixel values (e.g. 100,7900,1000,3900) or relative values by appending a percentage sign (e.g. 10,90,10,90%)."));
205  return;
206  };
207  if (key.CmpNoCase("OutputExposure") == 0)
208  {
209  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
210  p->SetHelpString(_("Select which output exposure value should be used.\nFor automatic detection use Auto or give a fixed exposure value."));
211  return;
212  };
213  if (key.CmpNoCase("OutputType") == 0)
214  {
215  // split string into individual items
216  wxArrayString values = wxStringTokenize(value, ",");
217  for (size_t i = 0; i < values.size(); ++i)
218  {
219  // remove whitespace from begin and end
220  values[i].Trim(true).Trim(false);
221  }
222  wxPGProperty* p = m_grid->AppendIn(section, new wxMultiChoiceProperty(key, wxPG_LABEL, OutputTypeArray, values));
223  p->SetHelpString(_("Select which output types should be activated."));
224  return;
225  };
226  if (key.CmpNoCase("Blender") == 0)
227  {
228  wxPGProperty* p = m_grid->AppendIn(section, new wxEnumProperty(key, wxPG_LABEL, BlenderArray));
229  if (value.IsEmpty())
230  {
231  p->SetValue(BlenderArray[0]);
232  }
233  else
234  {
235  p->SetValue(value);
236  };
237  p->SetHelpString(_("Select which blender should be used (can be enblend or internal)."));
238  return;
239  };
240  if (key.CmpNoCase("BlenderArgs") == 0)
241  {
242  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
243  p->SetHelpString(_("Select which arguments should be passed to the blender."));
244  return;
245  };
246  if (key.CmpNoCase("LDRFileType") == 0)
247  {
248  wxPGProperty* p = m_grid->AppendIn(section, new wxEnumProperty(key, wxPG_LABEL, LDRFileTypeArray));
249  if (value.IsEmpty())
250  {
251  p->SetValue(LDRFileTypeArray[0]);
252  }
253  else
254  {
255  p->SetValue(value);
256  };
257  p->SetHelpString(_("Select fileformat for LDR output should be set."));
258  return;
259  };
260  if (key.CmpNoCase("LDRCompression") == 0)
261  {
262  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
263  p->SetHelpString(_("Select which compressions should be used for LDR images.\nFor TIFF images NONE, PACKBITS, LZW and DEFLATE are supported.\nFor JPEG image give a number as quality (0-100)."));
264  return;
265  };
266  if (key.CmpNoCase("HDRFileType") == 0)
267  {
268  wxPGProperty* p = m_grid->AppendIn(section, new wxEnumProperty(key, wxPG_LABEL, HDRFileTypeArray));
269  if (value.IsEmpty())
270  {
271  p->SetValue(HDRFileTypeArray[0]);
272  }
273  else
274  {
275  p->SetValue(value);
276  };
277  p->SetHelpString(_("Select fileformat for HDR output should be set."));
278  return;
279  };
280  if (key.CmpNoCase("HDRCompression") == 0)
281  {
282  wxPGProperty* p = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
283  p->SetHelpString(_("Select which compressions should be used for HDR images.\nFor TIFF images NONE, PACKBITS, LZW and DEFLATE are supported."));
284  return;
285  }
286  // fall-through, should not be called
287  wxPGProperty* newProperty = m_grid->AppendIn(section, new wxStringProperty(key, wxPG_LABEL, value));
288 };
289 
291 {
292  wxFileName iniFilename = GetIniFileName();
293  wxInputStream* iniStream{ nullptr };
294 
295  if (iniFilename.FileExists())
296  {
297  iniStream = new wxFileInputStream(iniFilename.GetFullPath());
298  if (!iniStream->IsOk())
299  {
300  delete iniStream;
301  iniStream = nullptr;
302  }
303  };
304  if (iniStream == nullptr)
305  {
306  iniStream = new wxStringInputStream(defaultIni);
307  };
308  // now read from stream
309  wxFileConfig iniFile(*iniStream);
310  m_grid->Freeze();
311  wxString section;
312  long index;
313  bool hasSections = iniFile.GetFirstGroup(section, index);
314  while (hasSections)
315  {
316  wxPGProperty* pgSection = new wxPropertyCategory(section);
317  m_grid->Append(pgSection);
318  iniFile.SetPath(section);
319  wxString key;
320  long indexKey;
321  bool hasKeys = iniFile.GetFirstEntry(key, indexKey);
322  while (hasKeys)
323  {
324  AddKey(pgSection, key, iniFile.Read(key, wxEmptyString));
325  hasKeys = iniFile.GetNextEntry(key, indexKey);
326  };
327  iniFile.SetPath("/");
328  hasSections = iniFile.GetNextGroup(section, index);
329  };
330  m_grid->Thaw();
331  delete iniStream;
332 };
333 
335 {
336  wxFileConfig iniFile;
337  // iterate all entries
338  for (wxPropertyGridIterator it = m_grid->GetIterator(wxPG_ITERATE_ALL); !it.AtEnd(); it++)
339  {
340  wxPGProperty* p = it.GetProperty();
341  if (!p)
342  {
343  continue;
344  };
345  if (p->IsCategory())
346  {
347  iniFile.SetPath("/" + p->GetLabel());
348  }
349  else
350  {
351  wxVariant value = p->GetValue();
352  if (!value.IsNull())
353  {
354  if (value.GetType().CmpNoCase("string") == 0)
355  {
356  const wxString s(value.GetString());
357  iniFile.Write(p->GetLabel(), s);
358  }
359  else
360  {
361  if (value.GetType().CmpNoCase("long") == 0)
362  {
363  const long i = value.GetLong();
364  wxPGChoices choices = p->GetChoices();
365  iniFile.Write(p->GetLabel(), choices[i].GetText());
366  }
367  else
368  {
369  if (value.GetType().CmpNoCase("arrstring") == 0)
370  {
371  wxArrayString values = value.GetArrayString();
372  wxString s;
373  for (size_t i = 0; i < values.size(); ++i)
374  {
375  s << values[i] << ",";
376  };
377  if (!s.IsEmpty())
378  {
379  // remove the last separator
380  s.RemoveLast();
381  };
382  iniFile.Write(p->GetLabel(), s);
383  };
384  };
385  };
386  };
387  };
388  };
389  // finally write to disc
390  wxFileOutputStream iniStream(GetIniFileName().GetFullPath());
391  bool success = true;
392  if (iniStream.IsOk())
393  {
394  success = iniFile.Save(iniStream);
395  };
396  if (!success)
397  {
398  wxMessageBox(wxString::Format(_("Could not save ini file \"%s\"."), GetIniFileName().GetFullPath()), _("Error"), wxOK | wxICON_ERROR, this);
399  };
400 }
401 
403 {
404  return wxFileName(hugin_utils::GetUserAppDataDir(), "output.ini");
405 }
406 
407 void EditOutputIniDialog::BuildAddContextMenu(wxMenu& menu, wxPGProperty* category, const bool addSeparator)
408 {
409  // first get list of known values
410  wxArrayString knownValues=GetChildren(category);
411  m_currentSection = category;
412  wxMenuItem* item = menu.Append(wxID_ANY, _("Add condition"));
413  Bind(wxEVT_MENU, &EditOutputIniDialog::OnAddCondition, this, item->GetId());
414  wxString val;
415 #define ADDKEYITEM(s) val=s;\
416  if (!contains(knownValues, val))\
417  {\
418  item = menu.Append(wxID_ANY, wxString::Format(_("Add %s"), val.c_str()));\
419  Bind(wxEVT_MENU, [this, category, val](wxCommandEvent&) { AddKey(category, val, wxEmptyString); m_grid->RefreshGrid(); }, item->GetId());\
420  };
421  ADDKEYITEM("Projection")
422  ADDKEYITEM("FOV")
423  ADDKEYITEM("Canvas")
424  ADDKEYITEM("Crop")
425  ADDKEYITEM("OutputExposure")
426  ADDKEYITEM("OutputType")
427  ADDKEYITEM("Blender")
428  ADDKEYITEM("BlenderArgs")
429  ADDKEYITEM("LDRFileType")
430  ADDKEYITEM("LDRCompression")
431  ADDKEYITEM("HDRFileType")
432  ADDKEYITEM("HDRCompression")
433 #undef ADDKEYITEM
434  // and finally a separator
435  if (addSeparator)
436  {
437  menu.AppendSeparator();
438  };
439 }
440 
441 void EditOutputIniDialog::OnContextMenu(wxContextMenuEvent& e)
442 {
443  wxPropertyGridHitTestResult hitTest = m_grid->GetGrid()->HitTest(m_grid->GetGrid()->CalcScrolledPosition(m_grid->ScreenToClient(e.GetPosition())));
444  wxMenu contextMenu;
445  wxMenuItem* menuItem = contextMenu.Append(wxID_ANY, _("Create new section"));
446  Bind(wxEVT_MENU, &EditOutputIniDialog::OnAddSection, this, menuItem->GetId());
447  contextMenu.AppendSeparator();
448  wxPGProperty* prop = hitTest.GetProperty();
449  if (prop)
450  {
451  if (prop->IsCategory())
452  {
453  BuildAddContextMenu(contextMenu, prop, true);
454  // rename section menu item
455  menuItem = contextMenu.Append(wxID_ANY, wxString::Format(_("Rename section %s"), prop->GetLabel().c_str()));
456  Bind(wxEVT_MENU, &EditOutputIniDialog::OnRenameSection, this, menuItem->GetId());
457  }
458  else
459  {
460  BuildAddContextMenu(contextMenu, prop->GetParent(), true);
461  };
462  // remove section/item menu item
463  if (prop->IsCategory())
464  {
465  menuItem = contextMenu.Append(wxID_ANY, wxString::Format(_("Remove section %s"), prop->GetLabel().c_str()));
466  }
467  else
468  {
469  menuItem = contextMenu.Append(wxID_ANY, wxString::Format(_("Remove item %s"), prop->GetLabel().c_str()));
470  };
471  Bind(wxEVT_MENU, [this, prop](wxCommandEvent&) { m_grid->DeleteProperty(prop); m_grid->RefreshGrid(); }, menuItem->GetId());
472  }
473  else
474  {
475  // user clicked below last entry, take last category as target
476  wxPGProperty* lastCategory{ nullptr };
477  for (wxPropertyGridIterator it = m_grid->GetGrid()->GetIterator(wxPG_ITERATE_CATEGORIES); !it.AtEnd(); it++)
478  {
479  lastCategory = it.GetProperty();
480  }
481  if(lastCategory)
482  {
483  BuildAddContextMenu(contextMenu, lastCategory, false);
484  };
485  };
486  // show popup menu
487  PopupMenu(&contextMenu);
488 }
489 
490 wxArrayString EditOutputIniDialog::GetSections() const
491 {
492  wxArrayString sections;
493  for (wxPropertyGridIterator it = m_grid->GetIterator(wxPG_ITERATE_CATEGORIES); !it.AtEnd(); it++)
494  {
495  sections.Add(it.GetProperty()->GetLabel());
496  };
497  return sections;
498 }
499 
500 wxArrayString EditOutputIniDialog::GetChildren(wxPGProperty* prop) const
501 {
502  wxArrayString values;
503  size_t counter = 0;
504  for (wxPropertyGridIterator it = m_grid->GetIterator(wxPG_ITERATE_DEFAULT, prop); !it.AtEnd() && counter < prop->GetChildCount(); it++, ++counter)
505  {
506  values.Add(it.GetProperty()->GetLabel());
507  };
508  return values;
509 }
510 
511 void EditOutputIniDialog::OnAddSection(wxCommandEvent& e)
512 {
513  // get list of known sections
514  const wxArrayString knownSections = GetSections();
515  // ask user for new name
516  wxTextEntryDialog dialog(this, _("Name of new section:"), _("Create new section"));
517  if (dialog.ShowModal() == wxID_OK)
518  {
519  const wxString newSection = dialog.GetValue();
520  // check if new name is unique
521  if (contains(knownSections, newSection))
522  {
523  wxMessageBox(wxString::Format(_("Section \"%s\" is already defined.\nPlease use another name."), newSection.c_str()), _("Duplicate value."), wxOK | wxICON_ERROR);
524  }
525  else
526  {
527  // all ok, finally add new section to grid
528  wxPGProperty* pgSection = new wxPropertyCategory(newSection);
529  m_grid->AppendIn(m_grid->GetPageRoot(0), pgSection);
530  m_grid->RefreshGrid();
531  };
532  };
533 }
534 
536 {
537  wxPGProperty* currentSection = m_grid->GetSelection();
538  if (!currentSection->IsCategory())
539  {
540  return;
541  };
542  // get list of known sections, ignore current selected section
543  wxArrayString knownSections=GetSections();
544  knownSections.Remove(currentSection->GetLabel());
545  // ask user for new name
546  wxTextEntryDialog dialog(this, _("New name of section:"), _("Create new section"), currentSection->GetLabel());
547  if (dialog.ShowModal() == wxID_OK)
548  {
549  const wxString newSection = dialog.GetValue();
550  // check if new name is unique
551  if (contains(knownSections, newSection))
552  {
553  wxMessageBox(wxString::Format(_("Section \"%s\" is already defined.\nPlease use another name."), newSection.c_str()), _("Duplicate value."), wxOK | wxICON_ERROR);
554  }
555  else
556  {
557  // all ok, finally add new section to grid
558  currentSection->SetLabel(newSection);
559  m_grid->RefreshGrid();
560  };
561  };
562 }
563 
564 void EditOutputIniDialog::OnAddCondition(wxCommandEvent& e)
565 {
566  wxArrayString children = GetChildren(m_currentSection);
567  // get currently highest used number on condition
568  size_t index = 0;
569  for (size_t i = 0; i < children.GetCount(); ++i)
570  {
571  if (children[i].SubString(0, 8).CmpNoCase("Condition") == 0)
572  {
573  long x;
574  if (children[i].Mid(9).ToLong(&x))
575  {
576  index = std::max<long>(index, x);
577  };
578  };
579  };
580  ++index;
581  const wxString val = wxString::Format("Condition%d", index);
582  AddKey(m_currentSection, val, wxEmptyString);
583  m_grid->RefreshGrid();
584 }
void ReadIni()
read the ini file and populate the control
void OnAddSection(wxCommandEvent &e)
add a new section
Definition of dialog for editing user-defined output ini file.
wxArrayString GetSections() const
return wxArrayString with all sections
void InitArrays()
wxPGProperty * m_currentSection
EditOutputIniDialog(wxWindow *parent)
Constructor, constructs dialog; restore last uses settings, size and position.
wxPropertyGridManager * m_grid
static wxArrayString OutputTypeArray
static wxArrayString HDRFileTypeArray
static wxArrayString LDRFileTypeArray
void BuildAddContextMenu(wxMenu &menu, wxPGProperty *category, const bool addSeparator)
function to build context menu with all missing entries
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition: huginApp.cpp:641
wxArrayString GetChildren(wxPGProperty *prop) const
return wxArrayString with all sub-entries of given wxPGProperty
wxFileName GetIniFileName()
return the filename of the default ini file
void StoreFramePosition(wxTopLevelWindow *frame, const wxString &basename)
Store window size and position in configfile/registry.
Definition: LensCalApp.cpp:212
void OnContextMenu(wxContextMenuEvent &e)
right click handler, show popup menu
bool contains(const wxArrayString &stringArray, const wxString &string, bool caseInSensitive=true)
check if given string is in wxArrayString, do comparison case insentive or case sensitive ...
void RestoreFramePosition(wxTopLevelWindow *frame, const wxString &basename)
Restore window size and position from configfile/registry.
Definition: LensCalApp.cpp:158
void OnOk(wxCommandEvent &e)
save ini and close dialog
static wxArrayString ProjectionArray
void OnAddCondition(wxCommandEvent &e)
adds a new condition to list
void AddKey(wxPGProperty *section, const wxString &key, const wxString &value)
add key with value to wxPropertyGrid, generate if necessary all sub properties
include file for the hugin project
void OnRenameSection(wxCommandEvent &e)
renames a new section
~EditOutputIniDialog()
destructor, saves size and position
std::string GetUserAppDataDir()
returns the directory for user specific Hugin settings, e.g.
Definition: utils.cpp:497
static const wxString defaultIni
default ini, if no one exists load this one
void WriteIni()
write the ini to disc
#define ADDKEYITEM(s)
static wxArrayString BlenderArray