chiark / gitweb /
Fix the print menu item.
[cura.git] / Cura / gui / mainWindow.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import wx
5 import os
6 import webbrowser
7
8 from Cura.gui import configBase
9 from Cura.gui import expertConfig
10 from Cura.gui import alterationPanel
11 from Cura.gui import pluginPanel
12 from Cura.gui import preferencesDialog
13 from Cura.gui import configWizard
14 from Cura.gui import firmwareInstall
15 from Cura.gui import simpleMode
16 from Cura.gui import sceneView
17 #from Cura.gui.tools import batchRun
18 from Cura.gui.util import dropTarget
19 from Cura.gui.tools import minecraftImport
20 from Cura.gui.tools import superformula
21 from Cura.util import profile
22 from Cura.util import version
23 from Cura.util import meshLoader
24
25 class mainWindow(wx.Frame):
26         def __init__(self):
27                 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
28
29                 self.extruderCount = int(profile.getPreference('extruder_amount'))
30
31                 wx.EVT_CLOSE(self, self.OnClose)
32
33                 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions()))
34
35                 self.normalModeOnlyItems = []
36
37                 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
38                 self.config = wx.FileConfig(appName="Cura", 
39                                                 localFilename=mruFile,
40                                                 style=wx.CONFIG_USE_LOCAL_FILE)
41                                                 
42                 self.ID_MRU_MODEL1, self.ID_MRU_MODEL2, self.ID_MRU_MODEL3, self.ID_MRU_MODEL4, self.ID_MRU_MODEL5, self.ID_MRU_MODEL6, self.ID_MRU_MODEL7, self.ID_MRU_MODEL8, self.ID_MRU_MODEL9, self.ID_MRU_MODEL10 = [wx.NewId() for line in xrange(10)]
43                 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
44                 self.config.SetPath("/ModelMRU")
45                 self.modelFileHistory.Load(self.config)
46
47                 self.ID_MRU_PROFILE1, self.ID_MRU_PROFILE2, self.ID_MRU_PROFILE3, self.ID_MRU_PROFILE4, self.ID_MRU_PROFILE5, self.ID_MRU_PROFILE6, self.ID_MRU_PROFILE7, self.ID_MRU_PROFILE8, self.ID_MRU_PROFILE9, self.ID_MRU_PROFILE10 = [wx.NewId() for line in xrange(10)]
48                 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
49                 self.config.SetPath("/ProfileMRU")
50                 self.profileFileHistory.Load(self.config)
51
52                 self.menubar = wx.MenuBar()
53                 self.fileMenu = wx.Menu()
54                 i = self.fileMenu.Append(-1, 'Load model file...\tCTRL+L')
55                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
56                 i = self.fileMenu.Append(-1, 'Save model...\tCTRL+S')
57                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
58                 i = self.fileMenu.Append(-1, 'Clear platform')
59                 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
60
61                 self.fileMenu.AppendSeparator()
62                 i = self.fileMenu.Append(-1, 'Print...\tCTRL+P')
63                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(1), i)
64                 i = self.fileMenu.Append(-1, 'Save GCode...')
65                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
66                 i = self.fileMenu.Append(-1, 'Show slice engine log...')
67                 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
68
69                 self.fileMenu.AppendSeparator()
70                 i = self.fileMenu.Append(-1, 'Open Profile...')
71                 self.normalModeOnlyItems.append(i)
72                 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
73                 i = self.fileMenu.Append(-1, 'Save Profile...')
74                 self.normalModeOnlyItems.append(i)
75                 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
76                 i = self.fileMenu.Append(-1, 'Load Profile from GCode...')
77                 self.normalModeOnlyItems.append(i)
78                 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
79                 self.fileMenu.AppendSeparator()
80                 i = self.fileMenu.Append(-1, 'Reset Profile to default')
81                 self.normalModeOnlyItems.append(i)
82                 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
83
84                 self.fileMenu.AppendSeparator()
85                 i = self.fileMenu.Append(-1, 'Preferences...\tCTRL+,')
86                 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
87                 self.fileMenu.AppendSeparator()
88
89                 # Model MRU list
90                 modelHistoryMenu = wx.Menu()
91                 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Model Files", modelHistoryMenu)
92                 self.modelFileHistory.UseMenu(modelHistoryMenu)
93                 self.modelFileHistory.AddFilesToMenu()
94                 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
95
96                 # Profle MRU list
97                 profileHistoryMenu = wx.Menu()
98                 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Profile Files", profileHistoryMenu)
99                 self.profileFileHistory.UseMenu(profileHistoryMenu)
100                 self.profileFileHistory.AddFilesToMenu()
101                 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
102                 
103                 self.fileMenu.AppendSeparator()
104                 i = self.fileMenu.Append(wx.ID_EXIT, 'Quit')
105                 self.Bind(wx.EVT_MENU, self.OnQuit, i)
106                 self.menubar.Append(self.fileMenu, '&File')
107
108                 toolsMenu = wx.Menu()
109                 i = toolsMenu.Append(-1, 'Switch to quickprint...')
110                 self.switchToQuickprintMenuItem = i
111                 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
112                 i = toolsMenu.Append(-1, 'Switch to full settings...')
113                 self.switchToNormalMenuItem = i
114                 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
115                 toolsMenu.AppendSeparator()
116                 #i = toolsMenu.Append(-1, 'Batch run...')
117                 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
118                 #self.normalModeOnlyItems.append(i)
119                 if minecraftImport.hasMinecraft():
120                         i = toolsMenu.Append(-1, 'Minecraft import...')
121                         self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
122                 i = toolsMenu.Append(-1, 'Super-shaper...')
123                 self.Bind(wx.EVT_MENU, self.OnSuperformula, i)
124                 self.menubar.Append(toolsMenu, 'Tools')
125
126                 expertMenu = wx.Menu()
127                 i = expertMenu.Append(-1, 'Open expert settings...')
128                 self.normalModeOnlyItems.append(i)
129                 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
130                 expertMenu.AppendSeparator()
131                 if firmwareInstall.getDefaultFirmware() is not None:
132                         i = expertMenu.Append(-1, 'Install default Marlin firmware')
133                         self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
134                 i = expertMenu.Append(-1, 'Install custom firmware')
135                 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
136                 expertMenu.AppendSeparator()
137                 i = expertMenu.Append(-1, 'Run first run wizard...')
138                 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
139                 i = expertMenu.Append(-1, 'Run bed leveling wizard...')
140                 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
141                 if self.extruderCount > 1:
142                         i = expertMenu.Append(-1, 'Run head offset wizard...')
143                         self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
144                 self.menubar.Append(expertMenu, 'Expert')
145
146                 helpMenu = wx.Menu()
147                 i = helpMenu.Append(-1, 'Online documentation...')
148                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
149                 i = helpMenu.Append(-1, 'Report a problem...')
150                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
151                 i = helpMenu.Append(-1, 'Check for update...')
152                 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
153                 i = helpMenu.Append(-1, 'About Cura...')
154                 self.Bind(wx.EVT_MENU, self.OnAbout, i)
155                 self.menubar.Append(helpMenu, 'Help')
156                 self.SetMenuBar(self.menubar)
157
158                 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
159                 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
160                 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
161                 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
162
163                 ##Gui components##
164                 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
165                 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
166
167                 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
168                 self.leftSizer.Add(self.simpleSettingsPanel)
169                 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
170                 self.leftPane.SetSizer(self.leftSizer)
171                 
172                 #Preview window
173                 self.scene = sceneView.SceneView(self.rightPane)
174
175                 #Main sizer, to position the preview window, buttons and tab control
176                 sizer = wx.BoxSizer()
177                 self.rightPane.SetSizer(sizer)
178                 sizer.Add(self.scene, 1, flag=wx.EXPAND)
179
180                 # Main window sizer
181                 sizer = wx.BoxSizer(wx.VERTICAL)
182                 self.SetSizer(sizer)
183                 sizer.Add(self.splitter, 1, wx.EXPAND)
184                 sizer.Layout()
185                 self.sizer = sizer
186
187                 self.updateProfileToControls()
188
189                 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
190
191                 self.simpleSettingsPanel.Show(False)
192                 self.normalSettingsPanel.Show(False)
193
194                 # Set default window size & position
195                 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
196                 self.Centre()
197
198                 # Restore the window position, size & state from the preferences file
199                 try:
200                         if profile.getPreference('window_maximized') == 'True':
201                                 self.Maximize(True)
202                         else:
203                                 posx = int(profile.getPreference('window_pos_x'))
204                                 posy = int(profile.getPreference('window_pos_y'))
205                                 width = int(profile.getPreference('window_width'))
206                                 height = int(profile.getPreference('window_height'))
207                                 if posx > 0 or posy > 0:
208                                         self.SetPosition((posx,posy))
209                                 if width > 0 and height > 0:
210                                         self.SetSize((width,height))
211                                 
212                         self.normalSashPos = int(profile.getPreference('window_normal_sash'))
213                 except:
214                         self.normalSashPos = 0
215                         self.Maximize(True)
216                 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
217                         self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
218
219                 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
220
221                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
222                         self.Centre()
223                 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
224                         self.Centre()
225                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
226                         self.SetSize((800,600))
227                         self.Centre()
228
229                 self.updateSliceMode()
230
231         def updateSliceMode(self):
232                 isSimple = profile.getPreference('startMode') == 'Simple'
233
234                 self.normalSettingsPanel.Show(not isSimple)
235                 self.simpleSettingsPanel.Show(isSimple)
236                 self.leftPane.Layout()
237
238                 for i in self.normalModeOnlyItems:
239                         i.Enable(not isSimple)
240                 self.switchToQuickprintMenuItem.Enable(not isSimple)
241                 self.switchToNormalMenuItem.Enable(isSimple)
242
243                 # Set splitter sash position & size
244                 if isSimple:
245                         # Save normal mode sash
246                         self.normalSashPos = self.splitter.GetSashPosition()
247                         
248                         # Change location of sash to width of quick mode pane 
249                         (width, height) = self.simpleSettingsPanel.GetSizer().GetSize() 
250                         self.splitter.SetSashPosition(width, True)
251                         
252                         # Disable sash
253                         self.splitter.SetSashSize(0)
254                 else:
255                         self.splitter.SetSashPosition(self.normalSashPos, True)
256                         # Enabled sash
257                         self.splitter.SetSashSize(4)
258                 self.scene.updateProfileToControls()
259                                                                 
260         def OnPreferences(self, e):
261                 prefDialog = preferencesDialog.preferencesDialog(self)
262                 prefDialog.Centre()
263                 prefDialog.Show()
264
265         def OnDropFiles(self, files):
266                 if len(files) > 0:
267                         profile.setPluginConfig([])
268                         self.updateProfileToControls()
269                 self.scene.loadScene(files)
270
271         def OnModelMRU(self, e):
272                 fileNum = e.GetId() - self.ID_MRU_MODEL1
273                 path = self.modelFileHistory.GetHistoryFile(fileNum)
274                 # Update Model MRU
275                 self.modelFileHistory.AddFileToHistory(path)  # move up the list
276                 self.config.SetPath("/ModelMRU")
277                 self.modelFileHistory.Save(self.config)
278                 self.config.Flush()
279                 # Load Model
280                 filelist = [ path ]
281                 self.scene.loadScene(filelist)
282
283         def addToModelMRU(self, file):
284                 self.modelFileHistory.AddFileToHistory(file)
285                 self.config.SetPath("/ModelMRU")
286                 self.modelFileHistory.Save(self.config)
287                 self.config.Flush()
288         
289         def OnProfileMRU(self, e):
290                 fileNum = e.GetId() - self.ID_MRU_PROFILE1
291                 path = self.profileFileHistory.GetHistoryFile(fileNum)
292                 # Update Profile MRU
293                 self.profileFileHistory.AddFileToHistory(path)  # move up the list
294                 self.config.SetPath("/ProfileMRU")
295                 self.profileFileHistory.Save(self.config)
296                 self.config.Flush()
297                 # Load Profile  
298                 profile.loadProfile(path)
299                 self.updateProfileToControls()
300
301         def addToProfileMRU(self, file):
302                 self.profileFileHistory.AddFileToHistory(file)
303                 self.config.SetPath("/ProfileMRU")
304                 self.profileFileHistory.Save(self.config)
305                 self.config.Flush()                     
306
307         def updateProfileToControls(self):
308                 self.scene.updateProfileToControls()
309                 self.normalSettingsPanel.updateProfileToControls()
310                 self.simpleSettingsPanel.updateProfileToControls()
311
312         def OnLoadProfile(self, e):
313                 dlg=wx.FileDialog(self, "Select profile file to load", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
314                 dlg.SetWildcard("ini files (*.ini)|*.ini")
315                 if dlg.ShowModal() == wx.ID_OK:
316                         profileFile = dlg.GetPath()
317                         profile.loadProfile(profileFile)
318                         self.updateProfileToControls()
319
320                         # Update the Profile MRU
321                         self.addToProfileMRU(profileFile)
322                 dlg.Destroy()
323
324         def OnLoadProfileFromGcode(self, e):
325                 dlg=wx.FileDialog(self, "Select gcode file to load profile from", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
326                 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
327                 if dlg.ShowModal() == wx.ID_OK:
328                         gcodeFile = dlg.GetPath()
329                         f = open(gcodeFile, 'r')
330                         hasProfile = False
331                         for line in f:
332                                 if line.startswith(';CURA_PROFILE_STRING:'):
333                                         profile.loadProfileFromString(line[line.find(':')+1:].strip())
334                                         hasProfile = True
335                         if hasProfile:
336                                 self.updateProfileToControls()
337                         else:
338                                 wx.MessageBox('No profile found in GCode file.\nThis feature only works with GCode files made by Cura 12.07 or newer.', 'Profile load error', wx.OK | wx.ICON_INFORMATION)
339                 dlg.Destroy()
340
341         def OnSaveProfile(self, e):
342                 dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
343                 dlg.SetWildcard("ini files (*.ini)|*.ini")
344                 if dlg.ShowModal() == wx.ID_OK:
345                         profileFile = dlg.GetPath()
346                         profile.saveProfile(profileFile)
347                 dlg.Destroy()
348
349         def OnResetProfile(self, e):
350                 dlg = wx.MessageDialog(self, 'This will reset all profile settings to defaults.\nUnless you have saved your current profile, all settings will be lost!\nDo you really want to reset?', 'Profile reset', wx.YES_NO | wx.ICON_QUESTION)
351                 result = dlg.ShowModal() == wx.ID_YES
352                 dlg.Destroy()
353                 if result:
354                         profile.resetProfile()
355                         self.updateProfileToControls()
356
357         def OnBatchRun(self, e):
358                 br = batchRun.batchRunWindow(self)
359                 br.Centre()
360                 br.Show(True)
361
362         def OnSimpleSwitch(self, e):
363                 profile.putPreference('startMode', 'Simple')
364                 self.updateSliceMode()
365
366         def OnNormalSwitch(self, e):
367                 profile.putPreference('startMode', 'Normal')
368                 self.updateSliceMode()
369
370         def OnDefaultMarlinFirmware(self, e):
371                 firmwareInstall.InstallFirmware()
372
373         def OnCustomFirmware(self, e):
374                 if profile.getPreference('machine_type') == 'ultimaker':
375                         wx.MessageBox('Warning: Installing a custom firmware does not guarantee that you machine will function correctly, and could damage your machine.', 'Firmware update', wx.OK | wx.ICON_EXCLAMATION)
376                 dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
377                 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
378                 if dlg.ShowModal() == wx.ID_OK:
379                         filename = dlg.GetPath()
380                         if not(os.path.exists(filename)):
381                                 return
382                         #For some reason my Ubuntu 10.10 crashes here.
383                         firmwareInstall.InstallFirmware(filename)
384
385         def OnFirstRunWizard(self, e):
386                 configWizard.configWizard()
387                 self.updateProfileToControls()
388
389         def OnBedLevelWizard(self, e):
390                 configWizard.bedLevelWizard()
391
392         def OnHeadOffsetWizard(self, e):
393                 configWizard.headOffsetWizard()
394
395         def OnExpertOpen(self, e):
396                 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
397                 ecw.Centre()
398                 ecw.Show()
399
400         def OnMinecraftImport(self, e):
401                 mi = minecraftImport.minecraftImportWindow(self)
402                 mi.Centre()
403                 mi.Show(True)
404
405         def OnSuperformula(self, e):
406                 sf = superformula.superformulaWindow(self)
407                 #sf = superformula.superformulaEvolver(self)
408                 sf.Centre()
409                 sf.Show(True)
410
411         def OnCheckForUpdate(self, e):
412                 newVersion = version.checkForNewerVersion()
413                 if newVersion is not None:
414                         if wx.MessageBox('A new version of Cura is available, would you like to download?', 'New version available', wx.YES_NO | wx.ICON_INFORMATION) == wx.YES:
415                                 webbrowser.open(newVersion)
416                 else:
417                         wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
418
419         def OnAbout(self, e):
420                 info = wx.AboutDialogInfo()
421                 info.SetName('Cura')
422                 info.SetDescription('End solution for Open Source Fused Filament Fabrication 3D printing.')
423                 info.SetWebSite('http://software.ultimaker.com/')
424                 info.SetCopyright('Copyright (C) David Braam')
425                 info.SetLicence("""
426     This program is free software: you can redistribute it and/or modify
427     it under the terms of the GNU Affero General Public License as published by
428     the Free Software Foundation, either version 3 of the License, or
429     (at your option) any later version.
430
431     This program is distributed in the hope that it will be useful,
432     but WITHOUT ANY WARRANTY; without even the implied warranty of
433     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
434     GNU Affero General Public License for more details.
435
436     You should have received a copy of the GNU Affero General Public License
437     along with this program.  If not, see <http://www.gnu.org/licenses/>.
438 """)
439                 wx.AboutBox(info)
440
441         def OnClose(self, e):
442                 profile.saveProfile(profile.getDefaultProfilePath())
443
444                 # Save the window position, size & state from the preferences file
445                 profile.putPreference('window_maximized', self.IsMaximized())
446                 if not self.IsMaximized() and not self.IsIconized():
447                         (posx, posy) = self.GetPosition()
448                         profile.putPreference('window_pos_x', posx)
449                         profile.putPreference('window_pos_y', posy)
450                         (width, height) = self.GetSize()
451                         profile.putPreference('window_width', width)
452                         profile.putPreference('window_height', height)                  
453                         
454                         # Save normal sash position.  If in normal mode (!simple mode), get last position of sash before saving it...
455                         isSimple = profile.getPreference('startMode') == 'Simple'
456                         if not isSimple:
457                                 self.normalSashPos = self.splitter.GetSashPosition()
458                         profile.putPreference('window_normal_sash', self.normalSashPos)
459
460                 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
461                 print "Closing down"
462                 self.scene.OnPaint = lambda e : e
463                 self.scene._slicer.cleanup()
464                 self.Destroy()
465
466         def OnQuit(self, e):
467                 self.Close()
468
469 class normalSettingsPanel(configBase.configPanelBase):
470         "Main user interface window"
471         def __init__(self, parent, callback = None):
472                 super(normalSettingsPanel, self).__init__(parent, callback)
473
474                 #Main tabs
475                 self.nb = wx.Notebook(self)
476                 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
477                 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
478
479                 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
480                 self._addSettingsToPanels('basic', left, right)
481                 self.SizeLabelWidths(left, right)
482                 
483                 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
484                 self._addSettingsToPanels('advanced', left, right)
485                 self.SizeLabelWidths(left, right)
486
487                 #Plugin page
488                 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
489                 if len(self.pluginPanel.pluginList) > 0:
490                         self.nb.AddPage(self.pluginPanel, "Plugins")
491                 else:
492                         self.pluginPanel.Show(False)
493
494                 #Alteration page
495                 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
496                 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
497
498                 self.Bind(wx.EVT_SIZE, self.OnSize)
499
500                 self.nb.SetSize(self.GetSize())
501                 self.UpdateSize(self.printPanel)
502                 self.UpdateSize(self.advancedPanel)
503
504         def _addSettingsToPanels(self, category, left, right):
505                 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
506
507                 p = left
508                 n = 0
509                 for title in profile.getSubCategoriesFor(category):
510                         n += 1 + len(profile.getSettingsForCategory(category, title))
511                         if n > count / 2:
512                                 p = right
513                         configBase.TitleRow(p, title)
514                         for s in profile.getSettingsForCategory(category, title):
515                                 if s.checkConditions():
516                                         configBase.SettingRow(p, s.getName())
517
518         def SizeLabelWidths(self, left, right):
519                 leftWidth = self.getLabelColumnWidth(left)
520                 rightWidth = self.getLabelColumnWidth(right)
521                 maxWidth = max(leftWidth, rightWidth)
522                 self.setLabelColumnWidth(left, maxWidth)
523                 self.setLabelColumnWidth(right, maxWidth)
524
525         def OnSize(self, e):
526                 # Make the size of the Notebook control the same size as this control
527                 self.nb.SetSize(self.GetSize())
528                 
529                 # Propegate the OnSize() event (just in case)
530                 e.Skip()
531                 
532                 # Perform out resize magic
533                 self.UpdateSize(self.printPanel)
534                 self.UpdateSize(self.advancedPanel)
535         
536         def UpdateSize(self, configPanel):
537                 sizer = configPanel.GetSizer()
538                 
539                 # Pseudocde
540                 # if horizontal:
541                 #     if width(col1) < best_width(col1) || width(col2) < best_width(col2):
542                 #         switch to vertical
543                 # else:
544                 #     if width(col1) > (best_width(col1) + best_width(col1)):
545                 #         switch to horizontal
546                 #
547                                 
548                 col1 = configPanel.leftPanel
549                 colSize1 = col1.GetSize()
550                 colBestSize1 = col1.GetBestSize()
551                 col2 = configPanel.rightPanel
552                 colSize2 = col2.GetSize()
553                 colBestSize2 = col2.GetBestSize()
554
555                 orientation = sizer.GetOrientation()
556                 
557                 if orientation == wx.HORIZONTAL:
558                         if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
559                                 configPanel.Freeze()
560                                 sizer = wx.BoxSizer(wx.VERTICAL)
561                                 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
562                                 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
563                                 configPanel.SetSizer(sizer)
564                                 #sizer.Layout()
565                                 configPanel.Layout()
566                                 self.Layout()
567                                 configPanel.Thaw()
568                 else:
569                         if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
570                                 configPanel.Freeze()
571                                 sizer = wx.BoxSizer(wx.HORIZONTAL)
572                                 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
573                                 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
574                                 configPanel.SetSizer(sizer)
575                                 #sizer.Layout()
576                                 configPanel.Layout()
577                                 self.Layout()
578                                 configPanel.Thaw()
579
580         def updateProfileToControls(self):
581                 super(normalSettingsPanel, self).updateProfileToControls()
582                 self.alterationPanel.updateProfileToControls()
583                 self.pluginPanel.updateProfileToControls()