chiark / gitweb /
Update code for translations, fix a few translation issues and add a few missing...
[cura.git] / Cura / gui / mainWindow.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import wx
5 import os
6 import webbrowser
7
8 from Cura.gui import configBase
9 from Cura.gui import expertConfig
10 from Cura.gui import alterationPanel
11 from Cura.gui import pluginPanel
12 from Cura.gui import preferencesDialog
13 from Cura.gui import configWizard
14 from Cura.gui import firmwareInstall
15 from Cura.gui import simpleMode
16 from Cura.gui import sceneView
17 from Cura.gui import aboutWindow
18 from Cura.gui.util import dropTarget
19 #from Cura.gui.tools import batchRun
20 from Cura.gui.tools import pidDebugger
21 from Cura.gui.tools import minecraftImport
22 from Cura.util import profile
23 from Cura.util import version
24 from Cura.util import meshLoader
25
26 class mainWindow(wx.Frame):
27         def __init__(self):
28                 super(mainWindow, self).__init__(None, title='Cura - ' + version.getVersion())
29
30                 self.extruderCount = int(profile.getMachineSetting('extruder_amount'))
31
32                 wx.EVT_CLOSE(self, self.OnClose)
33
34                 # allow dropping any file, restrict later
35                 self.SetDropTarget(dropTarget.FileDropTarget(self.OnDropFiles))
36
37                 self.normalModeOnlyItems = []
38
39                 mruFile = os.path.join(profile.getBasePath(), 'mru_filelist.ini')
40                 self.config = wx.FileConfig(appName="Cura",
41                                                 localFilename=mruFile,
42                                                 style=wx.CONFIG_USE_LOCAL_FILE)
43
44                 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)]
45                 self.modelFileHistory = wx.FileHistory(10, self.ID_MRU_MODEL1)
46                 self.config.SetPath("/ModelMRU")
47                 self.modelFileHistory.Load(self.config)
48
49                 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)]
50                 self.profileFileHistory = wx.FileHistory(10, self.ID_MRU_PROFILE1)
51                 self.config.SetPath("/ProfileMRU")
52                 self.profileFileHistory.Load(self.config)
53
54                 self.menubar = wx.MenuBar()
55                 self.fileMenu = wx.Menu()
56                 i = self.fileMenu.Append(-1, _("Load model file...\tCTRL+L"))
57                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showLoadModel(), i)
58                 i = self.fileMenu.Append(-1, _("Save model...\tCTRL+S"))
59                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveModel(), i)
60                 i = self.fileMenu.Append(-1, _("Clear platform"))
61                 self.Bind(wx.EVT_MENU, lambda e: self.scene.OnDeleteAll(e), i)
62
63                 self.fileMenu.AppendSeparator()
64                 i = self.fileMenu.Append(-1, _("Print...\tCTRL+P"))
65                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showPrintWindow(), i)
66                 i = self.fileMenu.Append(-1, _("Save GCode..."))
67                 self.Bind(wx.EVT_MENU, lambda e: self.scene.showSaveGCode(), i)
68                 i = self.fileMenu.Append(-1, _("Show slice engine log..."))
69                 self.Bind(wx.EVT_MENU, lambda e: self.scene._showSliceLog(), i)
70
71                 self.fileMenu.AppendSeparator()
72                 i = self.fileMenu.Append(-1, _("Open Profile..."))
73                 self.normalModeOnlyItems.append(i)
74                 self.Bind(wx.EVT_MENU, self.OnLoadProfile, i)
75                 i = self.fileMenu.Append(-1, _("Save Profile..."))
76                 self.normalModeOnlyItems.append(i)
77                 self.Bind(wx.EVT_MENU, self.OnSaveProfile, i)
78                 i = self.fileMenu.Append(-1, _("Load Profile from GCode..."))
79                 self.normalModeOnlyItems.append(i)
80                 self.Bind(wx.EVT_MENU, self.OnLoadProfileFromGcode, i)
81                 self.fileMenu.AppendSeparator()
82                 i = self.fileMenu.Append(-1, _("Reset Profile to default"))
83                 self.normalModeOnlyItems.append(i)
84                 self.Bind(wx.EVT_MENU, self.OnResetProfile, i)
85
86                 self.fileMenu.AppendSeparator()
87                 i = self.fileMenu.Append(-1, _("Preferences...\tCTRL+,"))
88                 self.Bind(wx.EVT_MENU, self.OnPreferences, i)
89                 i = self.fileMenu.Append(-1, _("Machine settings..."))
90                 self.Bind(wx.EVT_MENU, self.OnMachineSettings, i)
91                 self.fileMenu.AppendSeparator()
92
93                 # Model MRU list
94                 modelHistoryMenu = wx.Menu()
95                 self.fileMenu.AppendMenu(wx.NewId(), '&' + _("Recent Model Files"), modelHistoryMenu)
96                 self.modelFileHistory.UseMenu(modelHistoryMenu)
97                 self.modelFileHistory.AddFilesToMenu()
98                 self.Bind(wx.EVT_MENU_RANGE, self.OnModelMRU, id=self.ID_MRU_MODEL1, id2=self.ID_MRU_MODEL10)
99
100                 # Profle MRU list
101                 profileHistoryMenu = wx.Menu()
102                 self.fileMenu.AppendMenu(wx.NewId(), _("Recent Profile Files"), profileHistoryMenu)
103                 self.profileFileHistory.UseMenu(profileHistoryMenu)
104                 self.profileFileHistory.AddFilesToMenu()
105                 self.Bind(wx.EVT_MENU_RANGE, self.OnProfileMRU, id=self.ID_MRU_PROFILE1, id2=self.ID_MRU_PROFILE10)
106
107                 self.fileMenu.AppendSeparator()
108                 i = self.fileMenu.Append(wx.ID_EXIT, _("Quit"))
109                 self.Bind(wx.EVT_MENU, self.OnQuit, i)
110                 self.menubar.Append(self.fileMenu, '&' + _("File"))
111
112                 toolsMenu = wx.Menu()
113
114                 i = toolsMenu.Append(-1, _("Switch to quickprint..."))
115                 self.switchToQuickprintMenuItem = i
116                 self.Bind(wx.EVT_MENU, self.OnSimpleSwitch, i)
117
118                 i = toolsMenu.Append(-1, _("Switch to full settings..."))
119                 self.switchToNormalMenuItem = i
120                 self.Bind(wx.EVT_MENU, self.OnNormalSwitch, i)
121
122                 #toolsMenu.AppendSeparator()
123                 #i = toolsMenu.Append(-1, 'Batch run...')
124                 #self.Bind(wx.EVT_MENU, self.OnBatchRun, i)
125                 #self.normalModeOnlyItems.append(i)
126
127                 if minecraftImport.hasMinecraft():
128                         i = toolsMenu.Append(-1, _("Minecraft map import..."))
129                         self.Bind(wx.EVT_MENU, self.OnMinecraftImport, i)
130
131                 if version.isDevVersion():
132                         i = toolsMenu.Append(-1, _("PID Debugger..."))
133                         self.Bind(wx.EVT_MENU, self.OnPIDDebugger, i)
134
135                 i = toolsMenu.Append(-1, _("Copy profile to clipboard"))
136                 self.Bind(wx.EVT_MENU, self.onCopyProfileClipboard,i)
137                 self.menubar.Append(toolsMenu, _("Tools"))
138
139                 #Machine menu for machine configuration/tooling
140                 self.machineMenu = wx.Menu()
141                 self.updateMachineMenu()
142
143                 self.menubar.Append(self.machineMenu, _("Machine"))
144
145                 expertMenu = wx.Menu()
146                 i = expertMenu.Append(-1, _("Open expert settings..."))
147                 self.normalModeOnlyItems.append(i)
148                 self.Bind(wx.EVT_MENU, self.OnExpertOpen, i)
149                 expertMenu.AppendSeparator()
150                 i = expertMenu.Append(-1, _("Run first run wizard..."))
151                 self.Bind(wx.EVT_MENU, self.OnFirstRunWizard, i)
152                 i = expertMenu.Append(-1, _("Run bed leveling wizard..."))
153                 self.Bind(wx.EVT_MENU, self.OnBedLevelWizard, i)
154                 if self.extruderCount > 1:
155                         i = expertMenu.Append(-1, _("Run head offset wizard..."))
156                         self.Bind(wx.EVT_MENU, self.OnHeadOffsetWizard, i)
157
158                 self.menubar.Append(expertMenu, _("Expert"))
159
160                 helpMenu = wx.Menu()
161                 i = helpMenu.Append(-1, _("Online documentation..."))
162                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('http://daid.github.com/Cura'), i)
163                 i = helpMenu.Append(-1, _("Report a problem..."))
164                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://github.com/daid/Cura/issues'), i)
165                 i = helpMenu.Append(-1, _("Check for update..."))
166                 self.Bind(wx.EVT_MENU, self.OnCheckForUpdate, i)
167                 i = helpMenu.Append(-1, _("Open YouMagine website..."))
168                 self.Bind(wx.EVT_MENU, lambda e: webbrowser.open('https://www.youmagine.com/'), i)
169                 i = helpMenu.Append(-1, _("About Cura..."))
170                 self.Bind(wx.EVT_MENU, self.OnAbout, i)
171                 self.menubar.Append(helpMenu, _("Help"))
172                 self.SetMenuBar(self.menubar)
173
174                 self.splitter = wx.SplitterWindow(self, style = wx.SP_3D | wx.SP_LIVE_UPDATE)
175                 self.leftPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
176                 self.rightPane = wx.Panel(self.splitter, style=wx.BORDER_NONE)
177                 self.splitter.Bind(wx.EVT_SPLITTER_DCLICK, lambda evt: evt.Veto())
178
179                 ##Gui components##
180                 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
181                 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
182
183                 self.leftSizer = wx.BoxSizer(wx.VERTICAL)
184                 self.leftSizer.Add(self.simpleSettingsPanel, 1)
185                 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
186                 self.leftPane.SetSizer(self.leftSizer)
187
188                 #Preview window
189                 self.scene = sceneView.SceneView(self.rightPane)
190
191                 #Main sizer, to position the preview window, buttons and tab control
192                 sizer = wx.BoxSizer()
193                 self.rightPane.SetSizer(sizer)
194                 sizer.Add(self.scene, 1, flag=wx.EXPAND)
195
196                 # Main window sizer
197                 sizer = wx.BoxSizer(wx.VERTICAL)
198                 self.SetSizer(sizer)
199                 sizer.Add(self.splitter, 1, wx.EXPAND)
200                 sizer.Layout()
201                 self.sizer = sizer
202
203                 self.updateProfileToAllControls()
204
205                 self.SetBackgroundColour(self.normalSettingsPanel.GetBackgroundColour())
206
207                 self.simpleSettingsPanel.Show(False)
208                 self.normalSettingsPanel.Show(False)
209
210                 # Set default window size & position
211                 self.SetSize((wx.Display().GetClientArea().GetWidth()/2,wx.Display().GetClientArea().GetHeight()/2))
212                 self.Centre()
213
214                 #Timer set; used to check if profile is on the clipboard
215                 self.timer = wx.Timer(self)
216                 self.Bind(wx.EVT_TIMER, self.onTimer)
217                 self.timer.Start(1000)
218                 self.lastTriedClipboard = profile.getProfileString()
219
220                 # Restore the window position, size & state from the preferences file
221                 try:
222                         if profile.getPreference('window_maximized') == 'True':
223                                 self.Maximize(True)
224                         else:
225                                 posx = int(profile.getPreference('window_pos_x'))
226                                 posy = int(profile.getPreference('window_pos_y'))
227                                 width = int(profile.getPreference('window_width'))
228                                 height = int(profile.getPreference('window_height'))
229                                 if posx > 0 or posy > 0:
230                                         self.SetPosition((posx,posy))
231                                 if width > 0 and height > 0:
232                                         self.SetSize((width,height))
233
234                         self.normalSashPos = int(profile.getPreference('window_normal_sash'))
235                 except:
236                         self.normalSashPos = 0
237                         self.Maximize(True)
238                 if self.normalSashPos < self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5:
239                         self.normalSashPos = self.normalSettingsPanel.printPanel.GetBestSize()[0] + 5
240
241                 self.splitter.SplitVertically(self.leftPane, self.rightPane, self.normalSashPos)
242
243                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
244                         self.Centre()
245                 if wx.Display.GetFromPoint((self.GetPositionTuple()[0] + self.GetSizeTuple()[1], self.GetPositionTuple()[1] + self.GetSizeTuple()[1])) < 0:
246                         self.Centre()
247                 if wx.Display.GetFromPoint(self.GetPosition()) < 0:
248                         self.SetSize((800,600))
249                         self.Centre()
250
251                 self.updateSliceMode()
252
253         def onTimer(self, e):
254                 #Check if there is something in the clipboard
255                 profileString = ""
256                 try:
257                         if not wx.TheClipboard.IsOpened():
258                                 wx.TheClipboard.Open()
259                                 do = wx.TextDataObject()
260                                 if wx.TheClipboard.GetData(do):
261                                         profileString = do.GetText()
262                                 wx.TheClipboard.Close()
263
264                                 startTag = "CURA_PROFILE_STRING:"
265                                 if startTag in profileString:
266                                         #print "Found correct syntax on clipboard"
267                                         profileString = profileString.replace("\n","").strip()
268                                         profileString = profileString[profileString.find(startTag)+len(startTag):]
269                                         if profileString != self.lastTriedClipboard:
270                                                 print profileString
271                                                 self.lastTriedClipboard = profileString
272                                                 profile.setProfileFromString(profileString)
273                                                 self.scene.notification.message("Loaded new profile from clipboard.")
274                                                 self.updateProfileToAllControls()
275                 except:
276                         print "Unable to read from clipboard"
277
278
279         def updateSliceMode(self):
280                 isSimple = profile.getPreference('startMode') == 'Simple'
281
282                 self.normalSettingsPanel.Show(not isSimple)
283                 self.simpleSettingsPanel.Show(isSimple)
284                 self.leftPane.Layout()
285
286                 for i in self.normalModeOnlyItems:
287                         i.Enable(not isSimple)
288                 self.switchToQuickprintMenuItem.Enable(not isSimple)
289                 self.switchToNormalMenuItem.Enable(isSimple)
290
291                 # Set splitter sash position & size
292                 if isSimple:
293                         # Save normal mode sash
294                         self.normalSashPos = self.splitter.GetSashPosition()
295
296                         # Change location of sash to width of quick mode pane
297                         (width, height) = self.simpleSettingsPanel.GetSizer().GetSize()
298                         self.splitter.SetSashPosition(width, True)
299
300                         # Disable sash
301                         self.splitter.SetSashSize(0)
302                 else:
303                         self.splitter.SetSashPosition(self.normalSashPos, True)
304                         # Enabled sash
305                         self.splitter.SetSashSize(4)
306                 self.scene.updateProfileToControls()
307
308         def OnPreferences(self, e):
309                 prefDialog = preferencesDialog.preferencesDialog(self)
310                 prefDialog.Centre()
311                 prefDialog.Show()
312
313         def OnMachineSettings(self, e):
314                 prefDialog = preferencesDialog.machineSettingsDialog(self)
315                 prefDialog.Centre()
316                 prefDialog.Show()
317
318         def OnDropFiles(self, files):
319                 if len(files) > 0:
320                         profile.setPluginConfig([])
321                         self.updateProfileToAllControls()
322                 self.scene.loadFiles(files)
323
324         def OnModelMRU(self, e):
325                 fileNum = e.GetId() - self.ID_MRU_MODEL1
326                 path = self.modelFileHistory.GetHistoryFile(fileNum)
327                 # Update Model MRU
328                 self.modelFileHistory.AddFileToHistory(path)  # move up the list
329                 self.config.SetPath("/ModelMRU")
330                 self.modelFileHistory.Save(self.config)
331                 self.config.Flush()
332                 # Load Model
333                 profile.putPreference('lastFile', path)
334                 filelist = [ path ]
335                 self.scene.loadFiles(filelist)
336
337         def addToModelMRU(self, file):
338                 self.modelFileHistory.AddFileToHistory(file)
339                 self.config.SetPath("/ModelMRU")
340                 self.modelFileHistory.Save(self.config)
341                 self.config.Flush()
342
343         def OnProfileMRU(self, e):
344                 fileNum = e.GetId() - self.ID_MRU_PROFILE1
345                 path = self.profileFileHistory.GetHistoryFile(fileNum)
346                 # Update Profile MRU
347                 self.profileFileHistory.AddFileToHistory(path)  # move up the list
348                 self.config.SetPath("/ProfileMRU")
349                 self.profileFileHistory.Save(self.config)
350                 self.config.Flush()
351                 # Load Profile
352                 profile.loadProfile(path)
353                 self.updateProfileToAllControls()
354
355         def addToProfileMRU(self, file):
356                 self.profileFileHistory.AddFileToHistory(file)
357                 self.config.SetPath("/ProfileMRU")
358                 self.profileFileHistory.Save(self.config)
359                 self.config.Flush()
360
361         def updateProfileToAllControls(self):
362                 self.scene.updateProfileToControls()
363                 self.normalSettingsPanel.updateProfileToControls()
364                 self.simpleSettingsPanel.updateProfileToControls()
365
366         def reloadSettingPanels(self):
367                 self.leftSizer.Detach(self.simpleSettingsPanel)
368                 self.leftSizer.Detach(self.normalSettingsPanel)
369                 self.simpleSettingsPanel.Destroy()
370                 self.normalSettingsPanel.Destroy()
371                 self.simpleSettingsPanel = simpleMode.simpleModePanel(self.leftPane, lambda : self.scene.sceneUpdated())
372                 self.normalSettingsPanel = normalSettingsPanel(self.leftPane, lambda : self.scene.sceneUpdated())
373                 self.leftSizer.Add(self.simpleSettingsPanel, 1)
374                 self.leftSizer.Add(self.normalSettingsPanel, 1, wx.EXPAND)
375                 self.updateSliceMode()
376                 self.updateProfileToAllControls()
377
378         def updateMachineMenu(self):
379                 #Remove all items so we can rebuild the menu. Inserting items seems to cause crashes, so this is the safest way.
380                 for item in self.machineMenu.GetMenuItems():
381                         self.machineMenu.RemoveItem(item)
382
383                 #Add a menu item for each machine configuration.
384                 for n in xrange(0, profile.getMachineCount()):
385                         i = self.machineMenu.Append(n, profile.getMachineSetting('machine_name', n).title(), kind=wx.ITEM_RADIO)
386                         if n == int(profile.getPreferenceFloat('active_machine')):
387                                 i.Check(True)
388                         self.Bind(wx.EVT_MENU, lambda e: self.OnSelectMachine(e.GetId()), i)
389
390                 self.machineMenu.AppendSeparator()
391                 i = self.machineMenu.Append(-1, _("Add new machine..."))
392                 self.Bind(wx.EVT_MENU, self.OnAddNewMachine, i)
393
394                 #Add tools for machines.
395                 self.machineMenu.AppendSeparator()
396                 i = self.machineMenu.Append(-1, _("Install custom firmware"))
397                 self.Bind(wx.EVT_MENU, self.OnCustomFirmware, i)
398                 i = self.machineMenu.Append(-1, _("Install default Marlin firmware"))
399                 self.Bind(wx.EVT_MENU, self.OnDefaultMarlinFirmware, i)
400
401         def OnLoadProfile(self, e):
402                 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)
403                 dlg.SetWildcard("ini files (*.ini)|*.ini")
404                 if dlg.ShowModal() == wx.ID_OK:
405                         profileFile = dlg.GetPath()
406                         profile.loadProfile(profileFile)
407                         self.updateProfileToAllControls()
408
409                         # Update the Profile MRU
410                         self.addToProfileMRU(profileFile)
411                 dlg.Destroy()
412
413         def OnLoadProfileFromGcode(self, e):
414                 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)
415                 dlg.SetWildcard("gcode files (*.gcode)|*.gcode;*.g")
416                 if dlg.ShowModal() == wx.ID_OK:
417                         gcodeFile = dlg.GetPath()
418                         f = open(gcodeFile, 'r')
419                         hasProfile = False
420                         for line in f:
421                                 if line.startswith(';CURA_PROFILE_STRING:'):
422                                         profile.setProfileFromString(line[line.find(':')+1:].strip())
423                                         hasProfile = True
424                         if hasProfile:
425                                 self.updateProfileToAllControls()
426                         else:
427                                 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)
428                 dlg.Destroy()
429
430         def OnSaveProfile(self, e):
431                 dlg=wx.FileDialog(self, _("Select profile file to save"), os.path.split(profile.getPreference('lastFile'))[0], style=wx.FD_SAVE)
432                 dlg.SetWildcard("ini files (*.ini)|*.ini")
433                 if dlg.ShowModal() == wx.ID_OK:
434                         profileFile = dlg.GetPath()
435                         profile.saveProfile(profileFile)
436                 dlg.Destroy()
437
438         def OnResetProfile(self, e):
439                 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)
440                 result = dlg.ShowModal() == wx.ID_YES
441                 dlg.Destroy()
442                 if result:
443                         profile.resetProfile()
444                         self.updateProfileToAllControls()
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.getMachineSetting('machine_type').startswith('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                 self.Hide()
471                 configWizard.configWizard()
472                 self.Show()
473                 self.reloadSettingPanels()
474
475         def OnAddNewMachine(self, e):
476                 self.Hide()
477                 profile.setActiveMachine(profile.getMachineCount())
478                 configWizard.configWizard(True)
479                 self.Show()
480                 self.reloadSettingPanels()
481                 self.updateMachineMenu()
482
483         def OnSelectMachine(self, index):
484                 profile.setActiveMachine(index)
485                 self.reloadSettingPanels()
486
487         def OnBedLevelWizard(self, e):
488                 configWizard.bedLevelWizard()
489
490         def OnHeadOffsetWizard(self, e):
491                 configWizard.headOffsetWizard()
492
493         def OnExpertOpen(self, e):
494                 ecw = expertConfig.expertConfigWindow(lambda : self.scene.sceneUpdated())
495                 ecw.Centre()
496                 ecw.Show()
497
498         def OnMinecraftImport(self, e):
499                 mi = minecraftImport.minecraftImportWindow(self)
500                 mi.Centre()
501                 mi.Show(True)
502
503         def OnPIDDebugger(self, e):
504                 debugger = pidDebugger.debuggerWindow(self)
505                 debugger.Centre()
506                 debugger.Show(True)
507
508         def onCopyProfileClipboard(self, e):
509                 try:
510                         if not wx.TheClipboard.IsOpened():
511                                 wx.TheClipboard.Open()
512                                 clipData = wx.TextDataObject()
513                                 self.lastTriedClipboard = profile.getProfileString()
514                                 profileString = profile.insertNewlines("CURA_PROFILE_STRING:" + self.lastTriedClipboard)
515                                 clipData.SetText(profileString)
516                                 wx.TheClipboard.SetData(clipData)
517                                 wx.TheClipboard.Close()
518                 except:
519                         print "Could not write to clipboard, unable to get ownership. Another program is using the clipboard."
520
521         def OnCheckForUpdate(self, e):
522                 newVersion = version.checkForNewerVersion()
523                 if newVersion is not None:
524                         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:
525                                 webbrowser.open(newVersion)
526                 else:
527                         wx.MessageBox(_("You are running the latest version of Cura!"), _("Awesome!"), wx.ICON_INFORMATION)
528
529         def OnAbout(self, e):
530                 aboutBox = aboutWindow.aboutWindow()
531                 aboutBox.Centre()
532                 aboutBox.Show()
533
534         def OnClose(self, e):
535                 profile.saveProfile(profile.getDefaultProfilePath())
536
537                 # Save the window position, size & state from the preferences file
538                 profile.putPreference('window_maximized', self.IsMaximized())
539                 if not self.IsMaximized() and not self.IsIconized():
540                         (posx, posy) = self.GetPosition()
541                         profile.putPreference('window_pos_x', posx)
542                         profile.putPreference('window_pos_y', posy)
543                         (width, height) = self.GetSize()
544                         profile.putPreference('window_width', width)
545                         profile.putPreference('window_height', height)
546
547                         # Save normal sash position.  If in normal mode (!simple mode), get last position of sash before saving it...
548                         isSimple = profile.getPreference('startMode') == 'Simple'
549                         if not isSimple:
550                                 self.normalSashPos = self.splitter.GetSashPosition()
551                         profile.putPreference('window_normal_sash', self.normalSashPos)
552
553                 #HACK: Set the paint function of the glCanvas to nothing so it won't keep refreshing. Which can keep wxWidgets from quiting.
554                 print "Closing down"
555                 self.scene.OnPaint = lambda e : e
556                 self.scene._slicer.cleanup()
557                 self.Destroy()
558
559         def OnQuit(self, e):
560                 self.Close()
561
562 class normalSettingsPanel(configBase.configPanelBase):
563         "Main user interface window"
564         def __init__(self, parent, callback = None):
565                 super(normalSettingsPanel, self).__init__(parent, callback)
566
567                 #Main tabs
568                 self.nb = wx.Notebook(self)
569                 self.SetSizer(wx.BoxSizer(wx.HORIZONTAL))
570                 self.GetSizer().Add(self.nb, 1, wx.EXPAND)
571
572                 (left, right, self.printPanel) = self.CreateDynamicConfigTab(self.nb, 'Basic')
573                 self._addSettingsToPanels('basic', left, right)
574                 self.SizeLabelWidths(left, right)
575
576                 (left, right, self.advancedPanel) = self.CreateDynamicConfigTab(self.nb, 'Advanced')
577                 self._addSettingsToPanels('advanced', left, right)
578                 self.SizeLabelWidths(left, right)
579
580                 #Plugin page
581                 self.pluginPanel = pluginPanel.pluginPanel(self.nb, callback)
582                 if len(self.pluginPanel.pluginList) > 0:
583                         self.nb.AddPage(self.pluginPanel, _("Plugins"))
584                 else:
585                         self.pluginPanel.Show(False)
586
587                 #Alteration page
588                 if profile.getMachineSetting('gcode_flavor') == 'UltiGCode':
589                         self.alterationPanel = None
590                 else:
591                         self.alterationPanel = alterationPanel.alterationPanel(self.nb, callback)
592                         self.nb.AddPage(self.alterationPanel, "Start/End-GCode")
593
594                 self.Bind(wx.EVT_SIZE, self.OnSize)
595
596                 self.nb.SetSize(self.GetSize())
597                 self.UpdateSize(self.printPanel)
598                 self.UpdateSize(self.advancedPanel)
599
600         def _addSettingsToPanels(self, category, left, right):
601                 count = len(profile.getSubCategoriesFor(category)) + len(profile.getSettingsForCategory(category))
602
603                 p = left
604                 n = 0
605                 for title in profile.getSubCategoriesFor(category):
606                         n += 1 + len(profile.getSettingsForCategory(category, title))
607                         if n > count / 2:
608                                 p = right
609                         configBase.TitleRow(p, _(title))
610                         for s in profile.getSettingsForCategory(category, title):
611                                 configBase.SettingRow(p, s.getName())
612
613         def SizeLabelWidths(self, left, right):
614                 leftWidth = self.getLabelColumnWidth(left)
615                 rightWidth = self.getLabelColumnWidth(right)
616                 maxWidth = max(leftWidth, rightWidth)
617                 self.setLabelColumnWidth(left, maxWidth)
618                 self.setLabelColumnWidth(right, maxWidth)
619
620         def OnSize(self, e):
621                 # Make the size of the Notebook control the same size as this control
622                 self.nb.SetSize(self.GetSize())
623
624                 # Propegate the OnSize() event (just in case)
625                 e.Skip()
626
627                 # Perform out resize magic
628                 self.UpdateSize(self.printPanel)
629                 self.UpdateSize(self.advancedPanel)
630
631         def UpdateSize(self, configPanel):
632                 sizer = configPanel.GetSizer()
633
634                 # Pseudocde
635                 # if horizontal:
636                 #     if width(col1) < best_width(col1) || width(col2) < best_width(col2):
637                 #         switch to vertical
638                 # else:
639                 #     if width(col1) > (best_width(col1) + best_width(col1)):
640                 #         switch to horizontal
641                 #
642
643                 col1 = configPanel.leftPanel
644                 colSize1 = col1.GetSize()
645                 colBestSize1 = col1.GetBestSize()
646                 col2 = configPanel.rightPanel
647                 colSize2 = col2.GetSize()
648                 colBestSize2 = col2.GetBestSize()
649
650                 orientation = sizer.GetOrientation()
651
652                 if orientation == wx.HORIZONTAL:
653                         if (colSize1[0] <= colBestSize1[0]) or (colSize2[0] <= colBestSize2[0]):
654                                 configPanel.Freeze()
655                                 sizer = wx.BoxSizer(wx.VERTICAL)
656                                 sizer.Add(configPanel.leftPanel, flag=wx.EXPAND)
657                                 sizer.Add(configPanel.rightPanel, flag=wx.EXPAND)
658                                 configPanel.SetSizer(sizer)
659                                 #sizer.Layout()
660                                 configPanel.Layout()
661                                 self.Layout()
662                                 configPanel.Thaw()
663                 else:
664                         if max(colSize1[0], colSize2[0]) > (colBestSize1[0] + colBestSize2[0]):
665                                 configPanel.Freeze()
666                                 sizer = wx.BoxSizer(wx.HORIZONTAL)
667                                 sizer.Add(configPanel.leftPanel, proportion=1, border=35, flag=wx.EXPAND)
668                                 sizer.Add(configPanel.rightPanel, proportion=1, flag=wx.EXPAND)
669                                 configPanel.SetSizer(sizer)
670                                 #sizer.Layout()
671                                 configPanel.Layout()
672                                 self.Layout()
673                                 configPanel.Thaw()
674
675         def updateProfileToControls(self):
676                 super(normalSettingsPanel, self).updateProfileToControls()
677                 if self.alterationPanel is not None:
678                         self.alterationPanel.updateProfileToControls()
679                 self.pluginPanel.updateProfileToControls()