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