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