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.util import profile
21 from Cura.util import version
22 from Cura.util import meshLoader
23
24 class mainWindow(wx.Frame):
25         def __init__(self):
26                 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
27
28                 self.extruderCount = int(profile.getPreference('extruder_amount'))
29
30                 wx.EVT_CLOSE(self, self.OnClose)
31
32                 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles, meshLoader.loadSupportedExtensions()))
33
34                 self.normalModeOnlyItems = []
35
36                 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
37                 self.config = wx.FileConfig(appName="Cura", 
38                                                 localFilename=mruFile,
39                                                 style=wx.CONFIG_USE_LOCAL_FILE)
40                                                 
41                 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)]
42                 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
43                 self.config.SetPath("/ModelMRU")
44                 self.modelFileHistory.Load(self.config)
45
46                 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)]
47                 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
48                 self.config.SetPath("/ProfileMRU")
49                 self.profileFileHistory.Load(self.config)
50
51                 self.menubar = wx.MenuBar()
52                 self.fileMenu = wx.Menu()
53                 i = self.fileMenu.Append(-1, 'Load model file...\tCTRL+L')
54                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
55                 i = self.fileMenu.Append(-1, 'Save model...\tCTRL+S')
56                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
57                 i = self.fileMenu.Append(-1, 'Clear platform')
58                 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
59
60                 self.fileMenu.AppendSeparator()
61                 i = self.fileMenu.Append(-1, 'Print...\tCTRL+P')
62                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
63                 i = self.fileMenu.Append(-1, 'Save GCode...')
64                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
65                 i = self.fileMenu.Append(-1, 'Show slice engine log...')
66                 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
67
68                 self.fileMenu.AppendSeparator()
69                 i = self.fileMenu.Append(-1, 'Open Profile...')
70                 self.normalModeOnlyItems.append(i)
71                 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
72                 i = self.fileMenu.Append(-1, 'Save Profile...')
73                 self.normalModeOnlyItems.append(i)
74                 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
75                 i = self.fileMenu.Append(-1, 'Load Profile from GCode...')
76                 self.normalModeOnlyItems.append(i)
77                 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
78                 self.fileMenu.AppendSeparator()
79                 i = self.fileMenu.Append(-1, 'Reset Profile to default')
80                 self.normalModeOnlyItems.append(i)
81                 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
82
83                 self.fileMenu.AppendSeparator()
84                 i = self.fileMenu.Append(-1, 'Preferences...\tCTRL+,')
85                 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
86                 self.fileMenu.AppendSeparator()
87
88                 # Model MRU list
89                 modelHistoryMenu = wx.Menu()
90                 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Model Files", modelHistoryMenu)
91                 self.modelFileHistory.UseMenu(modelHistoryMenu)
92                 self.modelFileHistory.AddFilesToMenu()
93                 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
94
95                 # Profle MRU list
96                 profileHistoryMenu = wx.Menu()
97                 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Profile Files", profileHistoryMenu)
98                 self.profileFileHistory.UseMenu(profileHistoryMenu)
99                 self.profileFileHistory.AddFilesToMenu()
100                 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
101                 
102                 self.fileMenu.AppendSeparator()
103                 i = self.fileMenu.Append(wx.ID_EXIT, 'Quit')
104                 self.Bind(wx.EVT_MENU, self.OnQuit, i)
105                 self.menubar.Append(self.fileMenu, '&File')
106
107                 toolsMenu = wx.Menu()
108                 i = toolsMenu.Append(-1, 'Switch to quickprint...')
109                 self.switchToQuickprintMenuItem = i
110                 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
111                 i = toolsMenu.Append(-1, 'Switch to full settings...')
112                 self.switchToNormalMenuItem = i
113                 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
114                 toolsMenu.AppendSeparator()
115                 #i = toolsMenu.Append(-1, 'Batch run...')
116                 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
117                 #self.normalModeOnlyItems.append(i)
118                 if minecraftImport.hasMinecraft():
119                         i = toolsMenu.Append(-1, 'Minecraft import...')
120                         self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
121                 self.menubar.Append(toolsMenu, 'Tools')
122
123                 expertMenu = wx.Menu()
124                 i = expertMenu.Append(-1, 'Open expert settings...')
125                 self.normalModeOnlyItems.append(i)
126                 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
127                 expertMenu.AppendSeparator()
128                 if firmwareInstall.getDefaultFirmware() is not None:
129                         i = expertMenu.Append(-1, 'Install default Marlin firmware')
130                         self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
131                 i = expertMenu.Append(-1, 'Install custom firmware')
132                 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
133                 expertMenu.AppendSeparator()
134                 i = expertMenu.Append(-1, 'Run first run wizard...')
135                 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
136                 i = expertMenu.Append(-1, 'Run bed leveling wizard...')
137                 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
138                 if self.extruderCount > 1:
139                         i = expertMenu.Append(-1, 'Run head offset wizard...')
140                         self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
141                 self.menubar.Append(expertMenu, 'Expert')
142
143                 helpMenu = wx.Menu()
144                 i = helpMenu.Append(-1, 'Online documentation...')
145                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
146                 i = helpMenu.Append(-1, 'Report a problem...')
147                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
148                 i = helpMenu.Append(-1, 'Check for update...')
149                 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
150                 i = helpMenu.Append(-1, 'About Cura...')
151                 self.Bind(wx.EVT_MENU, self.OnAbout, i)
152                 self.menubar.Append(helpMenu, 'Help')
153                 self.SetMenuBar(self.menubar)
154
155                 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
156                 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
157                 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
158                 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
159
160                 ##Gui components##
161                 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
162                 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
163
164                 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
165                 self.leftSizer.Add(self.simpleSettingsPanel)
166                 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
167                 self.leftPane.SetSizer(self.leftSizer)
168                 
169                 #Preview window
170                 self.scene = sceneView.SceneView(self.rightPane)
171
172                 #Main sizer, to position the preview window, buttons and tab control
173                 sizer = wx.BoxSizer()
174                 self.rightPane.SetSizer(sizer)
175                 sizer.Add(self.scene, 1, flag=wx.EXPAND)
176
177                 # Main window sizer
178                 sizer = wx.BoxSizer(wx.VERTICAL)
179                 self.SetSizer(sizer)
180                 sizer.Add(self.splitter, 1, wx.EXPAND)
181                 sizer.Layout()
182                 self.sizer = sizer
183
184                 self.updateProfileToControls()
185
186                 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
187
188                 self.simpleSettingsPanel.Show(False)
189                 self.normalSettingsPanel.Show(False)
190
191                 # Set default window size & position
192                 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
193                 self.Centre()
194
195                 # Restore the window position, size & state from the preferences file
196                 try:
197                         if profile.getPreference('window_maximized') == 'True':
198                                 self.Maximize(True)
199                         else:
200                                 posx = int(profile.getPreference('window_pos_x'))
201                                 posy = int(profile.getPreference('window_pos_y'))
202                                 width = int(profile.getPreference('window_width'))
203                                 height = int(profile.getPreference('window_height'))
204                                 if posx > 0 or posy > 0:
205                                         self.SetPosition((posx,posy))
206                                 if width > 0 and height > 0:
207                                         self.SetSize((width,height))
208                                 
209                         self.normalSashPos = int(profile.getPreference('window_normal_sash'))
210                 except:
211                         self.normalSashPos = 0
212                         self.Maximize(True)
213                 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
214                         self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
215
216                 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
217
218                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
219                         self.Centre()
220                 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
221                         self.Centre()
222                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
223                         self.SetSize((800,600))
224                         self.Centre()
225
226                 self.updateSliceMode()
227
228         def updateSliceMode(self):
229                 isSimple = profile.getPreference('startMode') == 'Simple'
230
231                 self.normalSettingsPanel.Show(not isSimple)
232                 self.simpleSettingsPanel.Show(isSimple)
233                 self.leftPane.Layout()
234
235                 for i in self.normalModeOnlyItems:
236                         i.Enable(not isSimple)
237                 self.switchToQuickprintMenuItem.Enable(not isSimple)
238                 self.switchToNormalMenuItem.Enable(isSimple)
239
240                 # Set splitter sash position & size
241                 if isSimple:
242                         # Save normal mode sash
243                         self.normalSashPos = self.splitter.GetSashPosition()
244                         
245                         # Change location of sash to width of quick mode pane 
246                         (width, height) = self.simpleSettingsPanel.GetSizer().GetSize() 
247                         self.splitter.SetSashPosition(width, True)
248                         
249                         # Disable sash
250                         self.splitter.SetSashSize(0)
251                 else:
252                         self.splitter.SetSashPosition(self.normalSashPos, True)
253                         # Enabled sash
254                         self.splitter.SetSashSize(4)
255                 self.scene.updateProfileToControls()
256                                                                 
257         def OnPreferences(self, e):
258                 prefDialog = preferencesDialog.preferencesDialog(self)
259                 prefDialog.Centre()
260                 prefDialog.Show()
261
262         def OnDropFiles(self, files):
263                 if len(files) > 0:
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                 profile.putPreference('lastFile', path)
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 OnCheckForUpdate(self, e):
404                 newVersion = version.checkForNewerVersion()
405                 if newVersion is not None:
406                         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:
407                                 webbrowser.open(newVersion)
408                 else:
409                         wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
410
411         def OnAbout(self, e):
412                 info = wx.AboutDialogInfo()
413                 info.SetName('Cura')
414                 info.SetDescription('End solution for Open Source Fused Filament Fabrication 3D printing.')
415                 info.SetWebSite('http://software.ultimaker.com/')
416                 info.SetCopyright('Copyright (C) David Braam')
417                 info.SetLicence("""
418     This program is free software: you can redistribute it and/or modify
419     it under the terms of the GNU Affero General Public License as published by
420     the Free Software Foundation, either version 3 of the License, or
421     (at your option) any later version.
422
423     This program is distributed in the hope that it will be useful,
424     but WITHOUT ANY WARRANTY; without even the implied warranty of
425     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
426     GNU Affero General Public License for more details.
427
428     You should have received a copy of the GNU Affero General Public License
429     along with this program.  If not, see <http://www.gnu.org/licenses/>.
430 """)
431                 wx.AboutBox(info)
432
433         def OnClose(self, e):
434                 profile.saveProfile(profile.getDefaultProfilePath())
435
436                 # Save the window position, size & state from the preferences file
437                 profile.putPreference('window_maximized', self.IsMaximized())
438                 if not self.IsMaximized() and not self.IsIconized():
439                         (posx, posy) = self.GetPosition()
440                         profile.putPreference('window_pos_x', posx)
441                         profile.putPreference('window_pos_y', posy)
442                         (width, height) = self.GetSize()
443                         profile.putPreference('window_width', width)
444                         profile.putPreference('window_height', height)                  
445                         
446                         # Save normal sash position.  If in normal mode (!simple mode), get last position of sash before saving it...
447                         isSimple = profile.getPreference('startMode') == 'Simple'
448                         if not isSimple:
449                                 self.normalSashPos = self.splitter.GetSashPosition()
450                         profile.putPreference('window_normal_sash', self.normalSashPos)
451
452                 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
453                 print "Closing down"
454                 self.scene.OnPaint = lambda e : e
455                 self.scene._slicer.cleanup()
456                 self.Destroy()
457
458         def OnQuit(self, e):
459                 self.Close()
460
461 class normalSettingsPanel(configBase.configPanelBase):
462         "Main user interface window"
463         def __init__(self, parent, callback = None):
464                 super(normalSettingsPanel, self).__init__(parent, callback)
465
466                 #Main tabs
467                 self.nb = wx.Notebook(self)
468                 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
469                 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
470
471                 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
472                 self._addSettingsToPanels('basic', left, right)
473                 self.SizeLabelWidths(left, right)
474                 
475                 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
476                 self._addSettingsToPanels('advanced', left, right)
477                 self.SizeLabelWidths(left, right)
478
479                 #Plugin page
480                 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
481                 if len(self.pluginPanel.pluginList) > 0:
482                         self.nb.AddPage(self.pluginPanel, "Plugins")
483                 else:
484                         self.pluginPanel.Show(False)
485
486                 #Alteration page
487                 self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
488                 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
489
490                 self.Bind(wx.EVT_SIZE, self.OnSize)
491
492                 self.nb.SetSize(self.GetSize())
493                 self.UpdateSize(self.printPanel)
494                 self.UpdateSize(self.advancedPanel)
495
496         def _addSettingsToPanels(self, category, left, right):
497                 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
498
499                 p = left
500                 n = 0
501                 for title in profile.getSubCategoriesFor(category):
502                         n += 1 + len(profile.getSettingsForCategory(category, title))
503                         if n > count / 2:
504                                 p = right
505                         configBase.TitleRow(p, title)
506                         for s in profile.getSettingsForCategory(category, title):
507                                 if s.checkConditions():
508                                         configBase.SettingRow(p, s.getName())
509
510         def SizeLabelWidths(self, left, right):
511                 leftWidth = self.getLabelColumnWidth(left)
512                 rightWidth = self.getLabelColumnWidth(right)
513                 maxWidth = max(leftWidth, rightWidth)
514                 self.setLabelColumnWidth(left, maxWidth)
515                 self.setLabelColumnWidth(right, maxWidth)
516
517         def OnSize(self, e):
518                 # Make the size of the Notebook control the same size as this control
519                 self.nb.SetSize(self.GetSize())
520                 
521                 # Propegate the OnSize() event (just in case)
522                 e.Skip()
523                 
524                 # Perform out resize magic
525                 self.UpdateSize(self.printPanel)
526                 self.UpdateSize(self.advancedPanel)
527         
528         def UpdateSize(self, configPanel):
529                 sizer = configPanel.GetSizer()
530                 
531                 # Pseudocde
532                 # if horizontal:
533                 #     if width(col1) < best_width(col1) || width(col2) < best_width(col2):
534                 #         switch to vertical
535                 # else:
536                 #     if width(col1) > (best_width(col1) + best_width(col1)):
537                 #         switch to horizontal
538                 #
539                                 
540                 col1 = configPanel.leftPanel
541                 colSize1 = col1.GetSize()
542                 colBestSize1 = col1.GetBestSize()
543                 col2 = configPanel.rightPanel
544                 colSize2 = col2.GetSize()
545                 colBestSize2 = col2.GetBestSize()
546
547                 orientation = sizer.GetOrientation()
548                 
549                 if orientation == wx.HORIZONTAL:
550                         if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
551                                 configPanel.Freeze()
552                                 sizer = wx.BoxSizer(wx.VERTICAL)
553                                 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
554                                 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
555                                 configPanel.SetSizer(sizer)
556                                 #sizer.Layout()
557                                 configPanel.Layout()
558                                 self.Layout()
559                                 configPanel.Thaw()
560                 else:
561                         if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
562                                 configPanel.Freeze()
563                                 sizer = wx.BoxSizer(wx.HORIZONTAL)
564                                 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
565                                 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
566                                 configPanel.SetSizer(sizer)
567                                 #sizer.Layout()
568                                 configPanel.Layout()
569                                 self.Layout()
570                                 configPanel.Thaw()
571
572         def updateProfileToControls(self):
573                 super(normalSettingsPanel, self).updateProfileToControls()
574                 self.alterationPanel.updateProfileToControls()
575                 self.pluginPanel.updateProfileToControls()