chiark / gitweb /
Add super-shaper tool..
[cura.git] / Cura / gui / mainWindow.py
1 from __future__ import absolute_import
2
3 import wx
4 import os
5 import webbrowser
6
7 from Cura.gui import configBase
8 from Cura.gui import expertConfig
9 from Cura.gui import alterationPanel
10 from Cura.gui import pluginPanel
11 from Cura.gui import preferencesDialog
12 from Cura.gui import configWizard
13 from Cura.gui import firmwareInstall
14 from Cura.gui import simpleMode
15 from Cura.gui import sceneView
16 from Cura.gui.tools import batchRun
17 from Cura.gui.util import dropTarget
18 from Cura.gui.tools import minecraftImport
19 from Cura.gui.tools import superformula
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 Steam Engine BETA - ' + 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.supportedExtensions()))
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(1), i)
55                 i = self.fileMenu.Append(-1, 'Print...\tCTRL+P')
56                 self.Bind(wx.EVT_MENU, lambda e: self.scene.ShowPrintWindow(), i)
57
58                 self.fileMenu.AppendSeparator()
59                 i = self.fileMenu.Append(-1, 'Open Profile...')
60                 self.normalModeOnlyItems.append(i)
61                 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
62                 i = self.fileMenu.Append(-1, 'Save Profile...')
63                 self.normalModeOnlyItems.append(i)
64                 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
65                 i = self.fileMenu.Append(-1, 'Load Profile from GCode...')
66                 self.normalModeOnlyItems.append(i)
67                 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
68                 self.fileMenu.AppendSeparator()
69                 i = self.fileMenu.Append(-1, 'Reset Profile to default')
70                 self.normalModeOnlyItems.append(i)
71                 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
72
73                 self.fileMenu.AppendSeparator()
74                 i = self.fileMenu.Append(-1, 'Preferences...\tCTRL+,')
75                 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
76                 self.fileMenu.AppendSeparator()
77
78                 # Model MRU list
79                 modelHistoryMenu = wx.Menu()
80                 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Model Files", modelHistoryMenu)
81                 self.modelFileHistory.UseMenu(modelHistoryMenu)
82                 self.modelFileHistory.AddFilesToMenu()
83                 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
84
85                 # Profle MRU list
86                 profileHistoryMenu = wx.Menu()
87                 self.fileMenu.AppendMenu(wx.NewId(), "&Recent Profile Files", profileHistoryMenu)
88                 self.profileFileHistory.UseMenu(profileHistoryMenu)
89                 self.profileFileHistory.AddFilesToMenu()
90                 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
91                 
92                 self.fileMenu.AppendSeparator()
93                 i = self.fileMenu.Append(wx.ID_EXIT, 'Quit')
94                 self.Bind(wx.EVT_MENU, self.OnQuit, i)
95                 self.menubar.Append(self.fileMenu, '&File')
96
97                 toolsMenu = wx.Menu()
98                 i = toolsMenu.Append(-1, 'Switch to quickprint...')
99                 self.switchToQuickprintMenuItem = i
100                 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
101                 i = toolsMenu.Append(-1, 'Switch to full settings...')
102                 self.switchToNormalMenuItem = i
103                 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
104                 toolsMenu.AppendSeparator()
105                 #i = toolsMenu.Append(-1, 'Batch run...')
106                 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
107                 #self.normalModeOnlyItems.append(i)
108                 if minecraftImport.hasMinecraft():
109                         i = toolsMenu.Append(-1, 'Minecraft import...')
110                         self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
111                 i = toolsMenu.Append(-1, 'Super-shaper...')
112                 self.Bind(wx.EVT_MENU, self.OnSuperformula, i)
113                 self.menubar.Append(toolsMenu, 'Tools')
114
115                 expertMenu = wx.Menu()
116                 i = expertMenu.Append(-1, 'Open expert settings...')
117                 self.normalModeOnlyItems.append(i)
118                 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
119                 expertMenu.AppendSeparator()
120                 if firmwareInstall.getDefaultFirmware() is not None:
121                         i = expertMenu.Append(-1, 'Install default Marlin firmware')
122                         self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
123                 i = expertMenu.Append(-1, 'Install custom firmware')
124                 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
125                 expertMenu.AppendSeparator()
126                 i = expertMenu.Append(-1, 'Run first run wizard...')
127                 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
128                 i = expertMenu.Append(-1, 'Run bed leveling wizard...')
129                 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
130                 self.menubar.Append(expertMenu, 'Expert')
131
132                 helpMenu = wx.Menu()
133                 i = helpMenu.Append(-1, 'Online documentation...')
134                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
135                 i = helpMenu.Append(-1, 'Report a problem...')
136                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
137                 i = helpMenu.Append(-1, 'Check for update...')
138                 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
139                 self.menubar.Append(helpMenu, 'Help')
140                 self.SetMenuBar(self.menubar)
141
142                 if profile.getPreference('lastFile') != '':
143                         self.filelist = profile.getPreference('lastFile').split(';')
144                 else:
145                         self.filelist = []
146
147                 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
148                 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
149                 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
150                 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
151
152                 ##Gui components##
153                 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
154                 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
155
156                 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
157                 self.leftSizer.Add(self.simpleSettingsPanel)
158                 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
159                 self.leftPane.SetSizer(self.leftSizer)
160                 
161                 #Preview window
162                 self.scene = sceneView.SceneView(self.rightPane)
163
164                 #Main sizer, to position the preview window, buttons and tab control
165                 sizer = wx.BoxSizer()
166                 self.rightPane.SetSizer(sizer)
167                 sizer.Add(self.scene, 1, flag=wx.EXPAND)
168
169                 # Main window sizer
170                 sizer = wx.BoxSizer(wx.VERTICAL)
171                 self.SetSizer(sizer)
172                 sizer.Add(self.splitter, 1, wx.EXPAND)
173                 sizer.Layout()
174                 self.sizer = sizer
175
176                 if len(self.filelist) > 0:
177                         self.scene.loadScene(self.filelist)
178
179                         # Update the Model MRU
180                         for idx in xrange(0, len(self.filelist)):
181                                 self.addToModelMRU(self.filelist[idx])
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                 self.Show(True)
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 OnExpertOpen(self, e):
390                 ecw = expertConfig.expertConfigWindow()
391                 ecw.Centre()
392                 ecw.ShowModal()
393                 self.scene.sceneUpdated()
394
395         def OnMinecraftImport(self, e):
396                 mi = minecraftImport.minecraftImportWindow(self)
397                 mi.Centre()
398                 mi.Show(True)
399
400         def OnSuperformula(self, e):
401                 sf = superformula.superformulaWindow(self)
402                 sf.Centre()
403                 sf.Show(True)
404
405         def OnCheckForUpdate(self, e):
406                 newVersion = version.checkForNewerVersion()
407                 if newVersion is not None:
408                         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:
409                                 webbrowser.open(newVersion)
410                 else:
411                         wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
412
413         def OnClose(self, e):
414                 profile.saveProfile(profile.getDefaultProfilePath())
415
416                 # Save the window position, size & state from the preferences file
417                 profile.putPreference('window_maximized', self.IsMaximized())
418                 if not self.IsMaximized() and not self.IsIconized():
419                         (posx, posy) = self.GetPosition()
420                         profile.putPreference('window_pos_x', posx)
421                         profile.putPreference('window_pos_y', posy)
422                         (width, height) = self.GetSize()
423                         profile.putPreference('window_width', width)
424                         profile.putPreference('window_height', height)                  
425                         
426                         # Save normal sash position.  If in normal mode (!simple mode), get last position of sash before saving it...
427                         isSimple = profile.getPreference('startMode') == 'Simple'
428                         if not isSimple:
429                                 self.normalSashPos = self.splitter.GetSashPosition()
430                         profile.putPreference('window_normal_sash', self.normalSashPos)
431
432                 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
433                 print "Closing down"
434                 self.scene.OnPaint = lambda e : e
435                 self.scene._slicer.cleanup()
436                 self.Destroy()
437
438         def OnQuit(self, e):
439                 self.Close()
440
441 class normalSettingsPanel(configBase.configPanelBase):
442         "Main user interface window"
443         def __init__(self, parent, callback = None):
444                 super(normalSettingsPanel, self).__init__(parent, callback)
445
446                 #Main tabs
447                 self.nb = wx.Notebook(self)
448                 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
449                 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
450
451                 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
452                 self._addSettingsToPanels('basic', left, right)
453                 self.SizeLabelWidths(left, right)
454                 
455                 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
456                 self._addSettingsToPanels('advanced', left, right)
457                 self.SizeLabelWidths(left, right)
458
459                 #Plugin page
460                 self.pluginPanel = pluginPanel.pluginPanel(self.nb)
461                 if len(self.pluginPanel.pluginList) > 0 and False:
462                         self.nb.AddPage(self.pluginPanel, "Plugins")
463                 else:
464                         self.pluginPanel.Show(False)
465
466                 #Alteration page
467                 self.alterationPanel = alterationPanel.alterationPanel(self.nb)
468                 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
469
470                 self.Bind(wx.EVT_SIZE, self.OnSize)
471
472                 self.nb.SetSize(self.GetSize())
473                 self.UpdateSize(self.printPanel)
474                 self.UpdateSize(self.advancedPanel)
475
476         def _addSettingsToPanels(self, category, left, right):
477                 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
478
479                 p = left
480                 n = 0
481                 for title in profile.getSubCategoriesFor(category):
482                         n += 1 + len(profile.getSettingsForCategory(category, title))
483                         if n > count / 2:
484                                 p = right
485                         configBase.TitleRow(p, title)
486                         for s in profile.getSettingsForCategory(category, title):
487                                 if s.checkConditions():
488                                         configBase.SettingRow(p, s.getName())
489
490         def SizeLabelWidths(self, left, right):
491                 leftWidth = self.getLabelColumnWidth(left)
492                 rightWidth = self.getLabelColumnWidth(right)
493                 maxWidth = max(leftWidth, rightWidth)
494                 self.setLabelColumnWidth(left, maxWidth)
495                 self.setLabelColumnWidth(right, maxWidth)
496
497         def OnSize(self, e):
498                 # Make the size of the Notebook control the same size as this control
499                 self.nb.SetSize(self.GetSize())
500                 
501                 # Propegate the OnSize() event (just in case)
502                 e.Skip()
503                 
504                 # Perform out resize magic
505                 self.UpdateSize(self.printPanel)
506                 self.UpdateSize(self.advancedPanel)
507         
508         def UpdateSize(self, configPanel):
509                 sizer = configPanel.GetSizer()
510                 
511                 # Pseudocde
512                 # if horizontal:
513                 #     if width(col1) < best_width(col1) || width(col2) < best_width(col2):
514                 #         switch to vertical
515                 # else:
516                 #     if width(col1) > (best_width(col1) + best_width(col1)):
517                 #         switch to horizontal
518                 #
519                                 
520                 col1 = configPanel.leftPanel
521                 colSize1 = col1.GetSize()
522                 colBestSize1 = col1.GetBestSize()
523                 col2 = configPanel.rightPanel
524                 colSize2 = col2.GetSize()
525                 colBestSize2 = col2.GetBestSize()
526
527                 orientation = sizer.GetOrientation()
528                 
529                 if orientation == wx.HORIZONTAL:
530                         if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
531                                 configPanel.Freeze()
532                                 sizer = wx.BoxSizer(wx.VERTICAL)
533                                 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
534                                 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
535                                 configPanel.SetSizer(sizer)
536                                 #sizer.Layout()
537                                 configPanel.Layout()
538                                 self.Layout()
539                                 configPanel.Thaw()
540                 else:
541                         if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
542                                 configPanel.Freeze()
543                                 sizer = wx.BoxSizer(wx.HORIZONTAL)
544                                 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
545                                 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
546                                 configPanel.SetSizer(sizer)
547                                 #sizer.Layout()
548                                 configPanel.Layout()
549                                 self.Layout()
550                                 configPanel.Thaw()
551
552         def updateProfileToControls(self):
553                 super(normalSettingsPanel, self).updateProfileToControls()
554                 self.alterationPanel.updateProfileToControls()
555                 self.pluginPanel.updateProfileToControls()