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