chiark / gitweb /
Merge branch 'master' into SteamEngine
[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                 try:
199                         if profile.getPreference('window_maximized') == 'True':
200                                 self.Maximize(True)
201                         else:
202                                 posx = int(profile.getPreference('window_pos_x'))
203                                 posy = int(profile.getPreference('window_pos_y'))
204                                 width = int(profile.getPreference('window_width'))
205                                 height = int(profile.getPreference('window_height'))
206                                 if posx > 0 or posy > 0:
207                                         self.SetPosition((posx,posy))
208                                 if width > 0 and height > 0:
209                                         self.SetSize((width,height))
210                                 
211                         self.normalSashPos = int(profile.getPreference('window_normal_sash'))
212                 except:
213                         self.normalSashPos = 0
214                         self.Maximize(True)
215                 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
216                         self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
217
218                 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
219
220                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
221                         self.Centre()
222                 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
223                         self.Centre()
224                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
225                         self.SetSize((800,600))
226                         self.Centre()
227
228                 self.updateSliceMode()
229
230                 self.Show(True)
231
232         def updateSliceMode(self):
233                 isSimple = profile.getPreference('startMode') == 'Simple'
234
235                 self.normalSettingsPanel.Show(not isSimple)
236                 self.simpleSettingsPanel.Show(isSimple)
237                 self.leftPane.Layout()
238
239                 for i in self.normalModeOnlyItems:
240                         i.Enable(not isSimple)
241                 self.switchToQuickprintMenuItem.Enable(not isSimple)
242                 self.switchToNormalMenuItem.Enable(isSimple)
243
244                 # Set splitter sash position & size
245                 if isSimple:
246                         # Save normal mode sash
247                         self.normalSashPos = self.splitter.GetSashPosition()
248                         
249                         # Change location of sash to width of quick mode pane 
250                         (width, height) = self.simpleSettingsPanel.GetSizer().GetSize() 
251                         self.splitter.SetSashPosition(width, True)
252                         
253                         # Disable sash
254                         self.splitter.SetSashSize(0)
255                 else:
256                         self.splitter.SetSashPosition(self.normalSashPos, True)
257                         # Enabled sash
258                         self.splitter.SetSashSize(4)
259                 self.scene.updateProfileToControls()
260                                                                 
261         def OnPreferences(self, e):
262                 prefDialog = preferencesDialog.preferencesDialog(self)
263                 prefDialog.Centre()
264                 prefDialog.Show(True)
265
266         def OnDropFiles(self, files):
267                 profile.putProfileSetting('model_matrix', '1,0,0,0,1,0,0,0,1')
268                 profile.setPluginConfig([])
269                 self.updateProfileToControls()
270                 self.scene.loadScene(files)
271
272         def OnPrint(self, e):
273                 if len(self.filelist) < 1:
274                         wx.MessageBox('You need to load a file and prepare it before you can print.', 'Print error', wx.OK | wx.ICON_INFORMATION)
275                         return
276                 if not os.path.exists(sliceRun.getExportFilename(self.filelist[0])):
277                         wx.MessageBox('You need to prepare a print before you can run the actual print.', 'Print error', wx.OK | wx.ICON_INFORMATION)
278                         return
279                 printWindow.printFile(sliceRun.getExportFilename(self.filelist[0]))
280
281         def OnModelMRU(self, e):
282                 fileNum = e.GetId() - self.ID_MRU_MODEL1
283                 path = self.modelFileHistory.GetHistoryFile(fileNum)
284                 # Update Model MRU
285                 self.modelFileHistory.AddFileToHistory(path)  # move up the list
286                 self.config.SetPath("/ModelMRU")
287                 self.modelFileHistory.Save(self.config)
288                 self.config.Flush()
289                 # Load Model
290                 filelist = [ path ]
291                 self.scene.loadScene(filelist)
292
293         def addToModelMRU(self, file):
294                 self.modelFileHistory.AddFileToHistory(file)
295                 self.config.SetPath("/ModelMRU")
296                 self.modelFileHistory.Save(self.config)
297                 self.config.Flush()
298         
299         def OnProfileMRU(self, e):
300                 fileNum = e.GetId() - self.ID_MRU_PROFILE1
301                 path = self.profileFileHistory.GetHistoryFile(fileNum)
302                 # Update Profile MRU
303                 self.profileFileHistory.AddFileToHistory(path)  # move up the list
304                 self.config.SetPath("/ProfileMRU")
305                 self.profileFileHistory.Save(self.config)
306                 self.config.Flush()
307                 # Load Profile  
308                 profile.loadProfile(path)
309                 self.updateProfileToControls()
310
311         def addToProfileMRU(self, file):
312                 self.profileFileHistory.AddFileToHistory(file)
313                 self.config.SetPath("/ProfileMRU")
314                 self.profileFileHistory.Save(self.config)
315                 self.config.Flush()                     
316
317         def updateProfileToControls(self):
318                 self.scene.updateProfileToControls()
319                 self.normalSettingsPanel.updateProfileToControls()
320                 self.simpleSettingsPanel.updateProfileToControls()
321
322         def OnLoadProfile(self, e):
323                 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)
324                 dlg.SetWildcard("ini files (*.ini)|*.ini")
325                 if dlg.ShowModal() == wx.ID_OK:
326                         profileFile = dlg.GetPath()
327                         profile.loadProfile(profileFile)
328                         self.updateProfileToControls()
329
330                         # Update the Profile MRU
331                         self.addToProfileMRU(profileFile)
332                 dlg.Destroy()
333
334         def OnLoadProfileFromGcode(self, e):
335                 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)
336                 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
337                 if dlg.ShowModal() == wx.ID_OK:
338                         gcodeFile = dlg.GetPath()
339                         f = open(gcodeFile, 'r')
340                         hasProfile = False
341                         for line in f:
342                                 if line.startswith(';CURA_PROFILE_STRING:'):
343                                         profile.loadProfileFromString(line[line.find(':')+1:].strip())
344                                         hasProfile = True
345                         if hasProfile:
346                                 self.updateProfileToControls()
347                         else:
348                                 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)
349                 dlg.Destroy()
350
351         def OnSaveProfile(self, e):
352                 dlg=wx.FileDialog(self, "Select profile file to save", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
353                 dlg.SetWildcard("ini files (*.ini)|*.ini")
354                 if dlg.ShowModal() == wx.ID_OK:
355                         profileFile = dlg.GetPath()
356                         profile.saveProfile(profileFile)
357                 dlg.Destroy()
358
359         def OnResetProfile(self, e):
360                 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)
361                 result = dlg.ShowModal() == wx.ID_YES
362                 dlg.Destroy()
363                 if result:
364                         profile.resetProfile()
365                         self.updateProfileToControls()
366
367         def OnBatchRun(self, e):
368                 br = batchRun.batchRunWindow(self)
369                 br.Centre()
370                 br.Show(True)
371
372         def OnSimpleSwitch(self, e):
373                 profile.putPreference('startMode', 'Simple')
374                 self.updateSliceMode()
375
376         def OnNormalSwitch(self, e):
377                 profile.putPreference('startMode', 'Normal')
378                 self.updateSliceMode()
379
380         def OnDefaultMarlinFirmware(self, e):
381                 firmwareInstall.InstallFirmware()
382
383         def OnCustomFirmware(self, e):
384                 if profile.getPreference('machine_type') == 'ultimaker':
385                         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)
386                 dlg=wx.FileDialog(self, "Open firmware to upload", os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
387                 dlg.SetWildcard("HEX file (*.hex)|*.hex;*.HEX")
388                 if dlg.ShowModal() == wx.ID_OK:
389                         filename = dlg.GetPath()
390                         if not(os.path.exists(filename)):
391                                 return
392                         #For some reason my Ubuntu 10.10 crashes here.
393                         firmwareInstall.InstallFirmware(filename)
394
395         def OnFirstRunWizard(self, e):
396                 configWizard.configWizard()
397                 self.updateProfileToControls()
398
399         def OnBedLevelWizard(self, e):
400                 configWizard.bedLevelWizard()
401
402         def OnExpertOpen(self, e):
403                 ecw = expertConfig.expertConfigWindow()
404                 ecw.Centre()
405                 ecw.Show(True)
406
407         def OnProjectPlanner(self, e):
408                 pp = projectPlanner.projectPlanner()
409                 pp.Centre()
410                 pp.Show(True)
411
412         def OnMinecraftImport(self, e):
413                 mi = minecraftImport.minecraftImportWindow(self)
414                 mi.Centre()
415                 mi.Show(True)
416
417         def OnCheckForUpdate(self, e):
418                 newVersion = version.checkForNewerVersion()
419                 if newVersion is not None:
420                         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:
421                                 webbrowser.open(newVersion)
422                 else:
423                         wx.MessageBox('You are running the latest version of Cura!', 'Awesome!', wx.ICON_INFORMATION)
424
425         def OnClose(self, e):
426                 profile.saveProfile(profile.getDefaultProfilePath())
427
428                 # Save the window position, size & state from the preferences file
429                 profile.putPreference('window_maximized', self.IsMaximized())
430                 if not self.IsMaximized() and not self.IsIconized():
431                         (posx, posy) = self.GetPosition()
432                         profile.putPreference('window_pos_x', posx)
433                         profile.putPreference('window_pos_y', posy)
434                         (width, height) = self.GetSize()
435                         profile.putPreference('window_width', width)
436                         profile.putPreference('window_height', height)                  
437                         
438                         # Save normal sash position.  If in normal mode (!simple mode), get last position of sash before saving it...
439                         isSimple = profile.getPreference('startMode') == 'Simple'
440                         if not isSimple:
441                                 self.normalSashPos = self.splitter.GetSashPosition()
442                         profile.putPreference('window_normal_sash', self.normalSashPos)
443
444                 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which keeps wxWidgets from quiting.
445                 print "Closing down"
446                 self.scene.OnPaint = lambda e : e
447                 self.Destroy()
448
449         def OnQuit(self, e):
450                 self.Close()
451
452 class normalSettingsPanel(configBase.configPanelBase):
453         "Main user interface window"
454         def __init__(self, parent, callback = None):
455                 super(normalSettingsPanel, self).__init__(parent, callback)
456
457                 #Main tabs
458                 self.nb = wx.Notebook(self)
459                 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
460                 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
461
462                 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
463                 self._addSettingsToPanels('basic', left, right)
464                 self.SizeLabelWidths(left, right)
465                 
466                 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
467                 self._addSettingsToPanels('advanced', left, right)
468                 self.SizeLabelWidths(left, right)
469
470                 #Plugin page
471                 self.pluginPanel = pluginPanel.pluginPanel(self.nb)
472                 if len(self.pluginPanel.pluginList) > 0:
473                         self.nb.AddPage(self.pluginPanel, "Plugins")
474                 else:
475                         self.pluginPanel.Show(False)
476
477                 #Alteration page
478                 self.alterationPanel = alterationPanel.alterationPanel(self.nb)
479                 self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
480
481                 self.Bind(wx.EVT_SIZE, self.OnSize)
482
483                 self.nb.SetSize(self.GetSize())
484                 self.UpdateSize(self.printPanel)
485                 self.UpdateSize(self.advancedPanel)
486
487         def _addSettingsToPanels(self, category, left, right):
488                 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
489
490                 p = left
491                 n = 0
492                 for title in profile.getSubCategoriesFor(category):
493                         n += 1 + len(profile.getSettingsForCategory(category, title))
494                         if n > count / 2:
495                                 p = right
496                         configBase.TitleRow(p, title)
497                         for s in profile.getSettingsForCategory(category, title):
498                                 if s.checkConditions():
499                                         configBase.SettingRow(p, s.getName())
500
501         def SizeLabelWidths(self, left, right):
502                 leftWidth = self.getLabelColumnWidth(left)
503                 rightWidth = self.getLabelColumnWidth(right)
504                 maxWidth = max(leftWidth, rightWidth)
505                 self.setLabelColumnWidth(left, maxWidth)
506                 self.setLabelColumnWidth(right, maxWidth)
507
508         def OnSize(self, e):
509                 # Make the size of the Notebook control the same size as this control
510                 self.nb.SetSize(self.GetSize())
511                 
512                 # Propegate the OnSize() event (just in case)
513                 e.Skip()
514                 
515                 # Perform out resize magic
516                 self.UpdateSize(self.printPanel)
517                 self.UpdateSize(self.advancedPanel)
518         
519         def UpdateSize(self, configPanel):
520                 sizer = configPanel.GetSizer()
521                 
522                 # Pseudocde
523                 # if horizontal:
524                 #     if width(col1) < best_width(col1) || width(col2) < best_width(col2):
525                 #         switch to vertical
526                 # else:
527                 #     if width(col1) > (best_width(col1) + best_width(col1)):
528                 #         switch to horizontal
529                 #
530                                 
531                 col1 = configPanel.leftPanel
532                 colSize1 = col1.GetSize()
533                 colBestSize1 = col1.GetBestSize()
534                 col2 = configPanel.rightPanel
535                 colSize2 = col2.GetSize()
536                 colBestSize2 = col2.GetBestSize()
537
538                 orientation = sizer.GetOrientation()
539                 
540                 if orientation == wx.HORIZONTAL:
541                         if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
542                                 configPanel.Freeze()
543                                 sizer = wx.BoxSizer(wx.VERTICAL)
544                                 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
545                                 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
546                                 configPanel.SetSizer(sizer)
547                                 #sizer.Layout()
548                                 configPanel.Layout()
549                                 self.Layout()
550                                 configPanel.Thaw()
551                 else:
552                         if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
553                                 configPanel.Freeze()
554                                 sizer = wx.BoxSizer(wx.HORIZONTAL)
555                                 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
556                                 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
557                                 configPanel.SetSizer(sizer)
558                                 #sizer.Layout()
559                                 configPanel.Layout()
560                                 self.Layout()
561                                 configPanel.Thaw()
562
563         def updateProfileToControls(self):
564                 super(normalSettingsPanel, self).updateProfileToControls()
565                 self.alterationPanel.updateProfileToControls()
566                 self.pluginPanel.updateProfileToControls()