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