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