chiark / gitweb /
Add clear-all to file menu.
[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(), 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                 profile.setPluginConfig([])
265                 self.updateProfileToControls()
266                 self.scene.loadScene(files)
267
268         def OnModelMRU(self, e):
269                 fileNum = e.GetId() - self.ID_MRU_MODEL1
270                 path = self.modelFileHistory.GetHistoryFile(fileNum)
271                 # Update Model MRU
272                 self.modelFileHistory.AddFileToHistory(path)  # move up the list
273                 self.config.SetPath("/ModelMRU")
274                 self.modelFileHistory.Save(self.config)
275                 self.config.Flush()
276                 # Load Model
277                 filelist = [ path ]
278                 self.scene.loadScene(filelist)
279
280         def addToModelMRU(self, file):
281                 self.modelFileHistory.AddFileToHistory(file)
282                 self.config.SetPath("/ModelMRU")
283                 self.modelFileHistory.Save(self.config)
284                 self.config.Flush()
285         
286         def OnProfileMRU(self, e):
287                 fileNum = e.GetId() - self.ID_MRU_PROFILE1
288                 path = self.profileFileHistory.GetHistoryFile(fileNum)
289                 # Update Profile MRU
290                 self.profileFileHistory.AddFileToHistory(path)  # move up the list
291                 self.config.SetPath("/ProfileMRU")
292                 self.profileFileHistory.Save(self.config)
293                 self.config.Flush()
294                 # Load Profile  
295                 profile.loadProfile(path)
296                 self.updateProfileToControls()
297
298         def addToProfileMRU(self, file):
299                 self.profileFileHistory.AddFileToHistory(file)
300                 self.config.SetPath("/ProfileMRU")
301                 self.profileFileHistory.Save(self.config)
302                 self.config.Flush()                     
303
304         def updateProfileToControls(self):
305                 self.scene.updateProfileToControls()
306                 self.normalSettingsPanel.updateProfileToControls()
307                 self.simpleSettingsPanel.updateProfileToControls()
308
309         def OnLoadProfile(self, e):
310                 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)
311                 dlg.SetWildcard("ini files (*.ini)|*.ini")
312                 if dlg.ShowModal() == wx.ID_OK:
313                         profileFile = dlg.GetPath()
314                         profile.loadProfile(profileFile)
315                         self.updateProfileToControls()
316
317                         # Update the Profile MRU
318                         self.addToProfileMRU(profileFile)
319                 dlg.Destroy()
320
321         def OnLoadProfileFromGcode(self, e):
322                 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)
323                 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
324                 if dlg.ShowModal() == wx.ID_OK:
325                         gcodeFile = dlg.GetPath()
326                         f = open(gcodeFile, 'r')
327                         hasProfile = False
328                         for line in f:
329                                 if line.startswith(';CURA_PROFILE_STRING:'):
330                                         profile.loadProfileFromString(line[line.find(':')+1:].strip())
331                                         hasProfile = True
332                         if hasProfile:
333                                 self.updateProfileToControls()
334                         else:
335                                 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)
336                 dlg.Destroy()
337
338         def OnSaveProfile(self, e):
339                 dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
340                 dlg.SetWildcard("ini files (*.ini)|*.ini")
341                 if dlg.ShowModal() == wx.ID_OK:
342                         profileFile = dlg.GetPath()
343                         profile.saveProfile(profileFile)
344                 dlg.Destroy()
345
346         def OnResetProfile(self, e):
347                 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)
348                 result = dlg.ShowModal() == wx.ID_YES
349                 dlg.Destroy()
350                 if result:
351                         profile.resetProfile()
352                         self.updateProfileToControls()
353
354         def OnBatchRun(self, e):
355                 br = batchRun.batchRunWindow(self)
356                 br.Centre()
357                 br.Show(True)
358
359         def OnSimpleSwitch(self, e):
360                 profile.putPreference('startMode', 'Simple')
361                 self.updateSliceMode()
362
363         def OnNormalSwitch(self, e):
364                 profile.putPreference('startMode', 'Normal')
365                 self.updateSliceMode()
366
367         def OnDefaultMarlinFirmware(self, e):
368                 firmwareInstall.InstallFirmware()
369
370         def OnCustomFirmware(self, e):
371                 if profile.getPreference('machine_type') == 'ultimaker':
372                         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)
373                 dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
374                 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
375                 if dlg.ShowModal() == wx.ID_OK:
376                         filename = dlg.GetPath()
377                         if not(os.path.exists(filename)):
378                                 return
379                         #For some reason my Ubuntu 10.10 crashes here.
380                         firmwareInstall.InstallFirmware(filename)
381
382         def OnFirstRunWizard(self, e):
383                 configWizard.configWizard()
384                 self.updateProfileToControls()
385
386         def OnBedLevelWizard(self, e):
387                 configWizard.bedLevelWizard()
388
389         def OnHeadOffsetWizard(self, e):
390                 configWizard.headOffsetWizard()
391
392         def OnExpertOpen(self, e):
393                 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
394                 ecw.Centre()
395                 ecw.Show()
396
397         def OnMinecraftImport(self, e):
398                 mi = minecraftImport.minecraftImportWindow(self)
399                 mi.Centre()
400                 mi.Show(True)
401
402         def OnSuperformula(self, e):
403                 sf = superformula.superformulaWindow(self)
404                 #sf = superformula.superformulaEvolver(self)
405                 sf.Centre()
406                 sf.Show(True)
407
408         def OnCheckForUpdate(self, e):
409                 newVersion = version.checkForNewerVersion()
410                 if newVersion is not None:
411                         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:
412                                 webbrowser.open(newVersion)
413                 else:
414                         wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
415
416         def OnAbout(self, e):
417                 info = wx.AboutDialogInfo()
418                 info.SetName('Cura')
419                 info.SetDescription('End solution for Open Source Fused Filament Fabrication 3D printing.')
420                 info.SetWebSite('http://software.ultimaker.com/')
421                 info.SetCopyright('Copyright (C) David Braam')
422                 info.SetLicence("""
423     This program is free software: you can redistribute it and/or modify
424     it under the terms of the GNU Affero General Public License as published by
425     the Free Software Foundation, either version 3 of the License, or
426     (at your option) any later version.
427
428     This program is distributed in the hope that it will be useful,
429     but WITHOUT ANY WARRANTY; without even the implied warranty of
430     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
431     GNU Affero General Public License for more details.
432
433     You should have received a copy of the GNU Affero General Public License
434     along with this program.  If not, see <http://www.gnu.org/licenses/>.
435 """)
436                 wx.AboutBox(info)
437
438         def OnClose(self, e):
439                 profile.saveProfile(profile.getDefaultProfilePath())
440
441                 # Save the window position, size & state from the preferences file
442                 profile.putPreference('window_maximized', self.IsMaximized())
443                 if not self.IsMaximized() and not self.IsIconized():
444                         (posx, posy) = self.GetPosition()
445                         profile.putPreference('window_pos_x', posx)
446                         profile.putPreference('window_pos_y', posy)
447                         (width, height) = self.GetSize()
448                         profile.putPreference('window_width', width)
449                         profile.putPreference('window_height', height)                  
450                         
451                         # Save normal sash position.  If in normal mode (!simple mode), get last position of sash before saving it...
452                         isSimple = profile.getPreference('startMode') == 'Simple'
453                         if not isSimple:
454                                 self.normalSashPos = self.splitter.GetSashPosition()
455                         profile.putPreference('window_normal_sash', self.normalSashPos)
456
457                 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
458                 print "Closing down"
459                 self.scene.OnPaint = lambda e : e
460                 self.scene._slicer.cleanup()
461                 self.Destroy()
462
463         def OnQuit(self, e):
464                 self.Close()
465
466 class normalSettingsPanel(configBase.configPanelBase):
467         "Main user interface window"
468         def __init__(self, parent, callback = None):
469                 super(normalSettingsPanel, self).__init__(parent, callback)
470
471                 #Main tabs
472                 self.nb = wx.Notebook(self)
473                 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
474                 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
475
476                 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
477                 self._addSettingsToPanels('basic', left, right)
478                 self.SizeLabelWidths(left, right)
479                 
480                 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
481                 self._addSettingsToPanels('advanced', left, right)
482                 self.SizeLabelWidths(left, right)
483
484                 #Plugin page
485                 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
486                 if len(self.pluginPanel.pluginList) > 0:
487                         self.nb.AddPage(self.pluginPanel, "Plugins")
488                 else:
489                         self.pluginPanel.Show(False)
490
491                 #Alteration page
492                 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
493                 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
494
495                 self.Bind(wx.EVT_SIZE, self.OnSize)
496
497                 self.nb.SetSize(self.GetSize())
498                 self.UpdateSize(self.printPanel)
499                 self.UpdateSize(self.advancedPanel)
500
501         def _addSettingsToPanels(self, category, left, right):
502                 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
503
504                 p = left
505                 n = 0
506                 for title in profile.getSubCategoriesFor(category):
507                         n += 1 + len(profile.getSettingsForCategory(category, title))
508                         if n > count / 2:
509                                 p = right
510                         configBase.TitleRow(p, title)
511                         for s in profile.getSettingsForCategory(category, title):
512                                 if s.checkConditions():
513                                         configBase.SettingRow(p, s.getName())
514
515         def SizeLabelWidths(self, left, right):
516                 leftWidth = self.getLabelColumnWidth(left)
517                 rightWidth = self.getLabelColumnWidth(right)
518                 maxWidth = max(leftWidth, rightWidth)
519                 self.setLabelColumnWidth(left, maxWidth)
520                 self.setLabelColumnWidth(right, maxWidth)
521
522         def OnSize(self, e):
523                 # Make the size of the Notebook control the same size as this control
524                 self.nb.SetSize(self.GetSize())
525                 
526                 # Propegate the OnSize() event (just in case)
527                 e.Skip()
528                 
529                 # Perform out resize magic
530                 self.UpdateSize(self.printPanel)
531                 self.UpdateSize(self.advancedPanel)
532         
533         def UpdateSize(self, configPanel):
534                 sizer = configPanel.GetSizer()
535                 
536                 # Pseudocde
537                 # if horizontal:
538                 #     if width(col1) < best_width(col1) || width(col2) < best_width(col2):
539                 #         switch to vertical
540                 # else:
541                 #     if width(col1) > (best_width(col1) + best_width(col1)):
542                 #         switch to horizontal
543                 #
544                                 
545                 col1 = configPanel.leftPanel
546                 colSize1 = col1.GetSize()
547                 colBestSize1 = col1.GetBestSize()
548                 col2 = configPanel.rightPanel
549                 colSize2 = col2.GetSize()
550                 colBestSize2 = col2.GetBestSize()
551
552                 orientation = sizer.GetOrientation()
553                 
554                 if orientation == wx.HORIZONTAL:
555                         if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
556                                 configPanel.Freeze()
557                                 sizer = wx.BoxSizer(wx.VERTICAL)
558                                 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
559                                 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
560                                 configPanel.SetSizer(sizer)
561                                 #sizer.Layout()
562                                 configPanel.Layout()
563                                 self.Layout()
564                                 configPanel.Thaw()
565                 else:
566                         if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
567                                 configPanel.Freeze()
568                                 sizer = wx.BoxSizer(wx.HORIZONTAL)
569                                 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
570                                 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
571                                 configPanel.SetSizer(sizer)
572                                 #sizer.Layout()
573                                 configPanel.Layout()
574                                 self.Layout()
575                                 configPanel.Thaw()
576
577         def updateProfileToControls(self):
578                 super(normalSettingsPanel, self).updateProfileToControls()
579                 self.alterationPanel.updateProfileToControls()
580                 self.pluginPanel.updateProfileToControls()