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