]> git.saurik.com Git - wxWidgets.git/blame - wxPython/samples/ide/activegrid/tool/ProjectEditor.py
Only freeze the splitter, the children will be automatically frozen too.
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / ProjectEditor.py
CommitLineData
1f780e48
RD
1#----------------------------------------------------------------------------
2# Name: ProjectEditor.py
3# Purpose: IDE-style Project Editor for wx.lib.pydocview
4#
5# Author: Peter Yared, Morgan Hua
6#
7# Created: 8/15/03
8# CVS-ID: $Id$
9# Copyright: (c) 2003, 2004, 2005 ActiveGrid, Inc.
10# License: wxWindows License
11#----------------------------------------------------------------------------
12
13import wx.lib.docview
14import wx.lib.pydocview
15import types
16import os
17import os.path
18import wx
19from wxPython.lib.rcsizer import RowColSizer
20import time
21import Service
1f780e48 22import sys
26ee3a06 23import activegrid.util.objutils
1f780e48 24import UICommon
b792147d 25import Wizard
6f1a3f9c 26import SVNService
1f780e48
RD
27from IDE import ACTIVEGRID_BASE_IDE
28if not ACTIVEGRID_BASE_IDE:
29 import ProcessModelEditor
6f1a3f9c 30from SVNService import SVN_INSTALLED
1f780e48
RD
31
32_ = wx.GetTranslation
33
34if wx.Platform == '__WXMSW__':
35 _WINDOWS = True
36else:
37 _WINDOWS = False
38
39
b792147d
RD
40#----------------------------------------------------------------------------
41# Constants
42#----------------------------------------------------------------------------
43SPACE = 10
44HALF_SPACE = 5
45
46
1f780e48
RD
47#----------------------------------------------------------------------------
48# XML Marshalling Methods
49#----------------------------------------------------------------------------
b792147d 50
1f780e48 51def load(fileObject):
26ee3a06 52 return activegrid.util.objutils.defaultLoad(fileObject)
1f780e48
RD
53
54def save(fileObject, projectModel):
26ee3a06 55 activegrid.util.objutils.defaultSave(fileObject, projectModel, prettyPrint=True)
1f780e48
RD
56
57
58#----------------------------------------------------------------------------
59# Classes
60#----------------------------------------------------------------------------
61
62class ProjectModel:
63 __xmlname__ = "projectmodel"
b792147d
RD
64 __xmlrename__ = { "_files":"files"}
65
1f780e48
RD
66
67 def __init__(self):
1f780e48 68 self._files = []
26ee3a06
RD
69
70 def initialize(self):
71 pass
b792147d
RD
72
73
1f780e48
RD
74class ProjectDocument(wx.lib.docview.Document):
75
b792147d 76
1f780e48
RD
77 def __init__(self):
78 wx.lib.docview.Document.__init__(self)
79 self._projectModel = ProjectModel()
bbf7159c 80
1f780e48
RD
81
82 def GetModel(self):
83 return self._projectModel
84
85
86 def OnCreate(self, path, flags):
87 projectService = wx.GetApp().GetService(ProjectService)
88 if projectService.GetView():
b792147d 89 # All project documents share the same view.
1f780e48
RD
90 view = projectService.GetView()
91 self.AddView(view)
b792147d
RD
92
93 if view.GetDocument():
94 # All project documents need to share the same command processor,
95 # to enable redo/undo of cross project document commands
96 cmdProcessor = view.GetDocument().GetCommandProcessor()
97 if cmdProcessor:
98 self.SetCommandProcessor(cmdProcessor)
1f780e48
RD
99 else:
100 view = self.GetDocumentTemplate().CreateView(self, flags)
101 projectService.SetView(view)
b792147d
RD
102
103
1f780e48
RD
104 return view
105
106
107 def LoadObject(self, fileObject):
108 self._projectModel = activegrid.tool.ProjectEditor.load(fileObject)
b792147d 109 self._projectModel._files = self.RelativeToAbsPath(self._projectModel._files)
1f780e48
RD
110 return True
111
112
113 def SaveObject(self, fileObject):
b792147d
RD
114 absPath = self._projectModel._files
115 self._projectModel._files = self.AbsToRelativePath(absPath) # temporarily change it to relative paths for saving
1f780e48 116 activegrid.tool.ProjectEditor.save(fileObject, self._projectModel)
b792147d 117 self._projectModel._files = absPath # swap it back to absolute path
1f780e48
RD
118 return True
119
120
b792147d
RD
121 def AbsToRelativePath(self, paths):
122 curPath = os.path.dirname(self.GetFilename())
123 curPathLen = len(curPath)
124
125 newFilePaths = []
126 for path in paths:
127 if path.startswith(curPath):
128 path = "." + path[curPathLen:] # use relative path
6f1a3f9c
RD
129 if os.sep != '/':
130 path = path.replace(os.sep, '/', -1) # always save out with '/' as path separator for cross-platform compatibility.
b792147d
RD
131 else:
132 pass # use absolute path
133 newFilePaths.append(path)
134 return newFilePaths
135
136
137 def RelativeToAbsPath(self, paths):
138 newFilePaths = []
139 for path in paths:
140 if path.startswith("."): # relative to project file
141 curPath = os.path.dirname(self.GetFilename())
142 path = os.path.normpath(os.path.join(curPath, path))
b792147d
RD
143 newFilePaths.append(path)
144 return newFilePaths
1f780e48
RD
145
146
147 def OnOpenDocument(self, filename):
148 view = self.GetFirstView()
149 frame = view.GetFrame()
150
151 if not os.path.exists(filename):
152 wx.GetApp().CloseSplash()
153 msgTitle = wx.GetApp().GetAppName()
154 if not msgTitle:
155 msgTitle = _("File Error")
156 wx.MessageBox(_("Could not find '%s'.") % filename,
157 msgTitle,
158 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
159 frame)
160 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
161
162 fileObject = file(filename, 'r')
163 try:
164 self.LoadObject(fileObject)
165 except:
166 wx.GetApp().CloseSplash()
167 msgTitle = wx.GetApp().GetAppName()
168 if not msgTitle:
169 msgTitle = _("File Error")
170 wx.MessageBox(_("Could not open '%s'. %s") % (wx.lib.docview.FileNameFromPath(filename), sys.exc_value),
171 msgTitle,
172 wx.OK | wx.ICON_EXCLAMATION | wx.STAY_ON_TOP,
173 frame)
174 return True # if we return False, the Project View is destroyed, Service windows shouldn't be destroyed
175
176 self.Modify(False)
1f780e48
RD
177 self.SetFilename(filename, True)
178 view.AddProjectToView(self)
bbf7159c 179 self.SetDocumentModificationDate()
1f780e48
RD
180 self.UpdateAllViews()
181 self._savedYet = True
182 view.Activate(True)
183 return True
184
185
186 def AddFile(self, file):
187 return self.AddFiles([file])
188
189
190 def AddFiles(self, files):
191 notAlreadyThereFiles = filter(lambda x: x not in self._projectModel._files, files) # Filter to the files that are not already in the project
192 if len(notAlreadyThereFiles) == 0:
193 self.UpdateAllViews(hint = ("select", self, files))
194 return False
195 else:
196 self._projectModel._files = self._projectModel._files + notAlreadyThereFiles
197 self.UpdateAllViews(hint = ("add", self, notAlreadyThereFiles))
198 self.Modify(True)
199 return True
200
201
202 def RemoveFile(self, file):
203 return self.RemoveFiles([file])
204
205
206 def RemoveFiles(self, files):
207 for file in files:
208 self._projectModel._files.remove(file)
209 self.UpdateAllViews(hint = ("remove", self, files))
210 self.Modify(True)
211 return True
212
213
214 def RenameFile(self, oldFile, newFile, isProject = False):
215 try:
216 if oldFile == newFile:
217 return False
218
219 # projects don't have to exist yet, so not required to rename old file,
220 # but files must exist, so we'll try to rename and allow exceptions to occur if can't.
221 if not isProject or (isProject and os.path.exists(oldFile)):
222 os.rename(oldFile, newFile)
223
224 if isProject:
225 documents = self.GetDocumentManager().GetDocuments()
226 for document in documents:
26ee3a06 227 if os.path.normcase(document.GetFilename()) == os.path.normcase(oldFile): # If the renamed document is open, update it
1f780e48
RD
228 document.SetFilename(newFile)
229 document.SetTitle(wx.lib.docview.FileNameFromPath(newFile))
230 document.UpdateAllViews(hint = ("rename", document, newFile))
231 else:
232 self.RemoveFile(oldFile)
233 self.AddFile(newFile)
234 documents = self.GetDocumentManager().GetDocuments()
235 for document in documents:
26ee3a06 236 if os.path.normcase(document.GetFilename()) == os.path.normcase(oldFile): # If the renamed document is open, update it
1f780e48
RD
237 document.SetFilename(newFile, notifyViews = True)
238 document.UpdateAllViews(hint = ("rename", document, newFile))
239 return True
240 except OSError, (code, message):
241 msgTitle = wx.GetApp().GetAppName()
242 if not msgTitle:
243 msgTitle = _("File Error")
244 wx.MessageBox("Could not rename '%s'. '%s'" % (wx.lib.docview.FileNameFromPath(oldFile), message),
245 msgTitle,
246 wx.OK | wx.ICON_EXCLAMATION,
247 self.GetFirstView().GetFrame())
248 return False
249
250
251 def GetFiles(self):
252 return self._projectModel._files
253
254
255 def IsFileInProject(self, filename):
256 return filename in self.GetFiles()
257
258
1f780e48
RD
259class NewProjectWizard(Wizard.BaseWizard):
260
261 WIZTITLE = _("New Project Wizard")
262
263 def __init__(self, parent):
264 self._parent = parent
265 self._fullProjectPath = None
266 Wizard.BaseWizard.__init__(self, parent, self.WIZTITLE)
267 self._projectLocationPage = self.CreateProjectLocation(self)
268 wx.wizard.EVT_WIZARD_PAGE_CHANGING(self, self.GetId(), self.OnWizPageChanging)
b792147d
RD
269
270
1f780e48
RD
271 def CreateProjectLocation(self,wizard):
272 page = Wizard.TitledWizardPage(wizard, _("Project File Location"))
273
274 page.GetSizer().Add(wx.StaticText(page, -1, _("\nSelect the directory and filename for the project.\n\n")))
275 self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, _("File Name:"), _("Directory:"), _("agp"), startingDirectory=os.getcwd())
276 page.GetSizer().Add(sizer, 1, flag=wx.EXPAND)
277
278 wizard.Layout()
279 wizard.FitToPage(page)
280 return page
281
b792147d 282
1f780e48
RD
283 def RunWizard(self, existingTables = None, existingRelationships = None):
284 status = wx.wizard.Wizard.RunWizard(self, self._projectLocationPage)
285 if status:
286 docManager = wx.GetApp().GetTopWindow().GetDocumentManager()
287 if os.path.exists(self._fullProjectPath):
288 # What if the document is already open and we're overwriting it?
289 documents = docManager.GetDocuments()
290 for document in documents:
26ee3a06 291 if os.path.normcase(document.GetFilename()) == os.path.normcase(self._fullProjectPath): # If the renamed document is open, update it
1f780e48
RD
292 document.DeleteAllViews()
293 break
294 os.remove(self._fullProjectPath)
295
296 for template in docManager.GetTemplates():
297 if template.GetDocumentType() == ProjectDocument:
298 doc = template.CreateDocument(self._fullProjectPath, flags = wx.lib.docview.DOC_NEW)
299 doc.OnSaveDocument(self._fullProjectPath)
300 view = doc.GetFirstView()
301 view.AddProjectToView(doc)
302 break
303
304 self.Destroy()
305 return status
306
307
308 def OnWizPageChanging(self, event):
309 if event.GetDirection(): # It's going forwards
310 if event.GetPage() == self._projectLocationPage:
311 if not self._fileValidation():
312 event.Veto()
313 return
314 self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(),'.agp'))
315
316
317
318 def OnShowCreatePages(self):
319 self.Hide()
320 import DataModelEditor
321 requestedPos = self.GetPositionTuple()
322 projectService = wx.GetApp().GetService(ProjectService)
323 projectView = projectService.GetView()
324
325 wiz = DataModelEditor.ImportExportWizard(projectView.GetFrame(), pos=requestedPos)
326 if wiz.RunWizard(dontDestroy=True):
327 self._schemaName.SetValue(wiz.GetSchemaFileName())
328 wiz.Destroy()
329 self.Show(True)
330
b792147d 331
1f780e48
RD
332class ProjectTemplate(wx.lib.docview.DocTemplate):
333
b792147d 334
1f780e48
RD
335 def CreateDocument(self, path, flags):
336 if path:
337 return wx.lib.docview.DocTemplate.CreateDocument(self, path, flags)
338 else:
339 wiz = NewProjectWizard(wx.GetApp().GetTopWindow())
340 wiz.RunWizard()
341 wiz.Destroy()
342 return None # never return the doc, otherwise docview will think it is a new file and rename it
343
b792147d 344
1f780e48
RD
345class ProjectAddFilesCommand(wx.lib.docview.Command):
346
b792147d 347
1f780e48
RD
348 def __init__(self, projectDoc, files):
349 wx.lib.docview.Command.__init__(self, canUndo = True)
350 self._projectDoc = projectDoc
351 self._files = files
352
353
354 def GetName(self):
355 if len(self._files) == 1:
b792147d 356 return _("Add File %s") % os.path.basename(self._files[0])
1f780e48
RD
357 else:
358 return _("Add Files")
359
360
361 def Do(self):
362 return self._projectDoc.AddFiles(self._files)
363
364
365 def Undo(self):
366 return self._projectDoc.RemoveFiles(self._files)
367
368
369class ProjectRemoveFilesCommand(wx.lib.docview.Command):
370
b792147d
RD
371
372 def __init__(self, files):
1f780e48 373 wx.lib.docview.Command.__init__(self, canUndo = True)
1f780e48
RD
374 self._files = files
375
376
377 def GetName(self):
378 if len(self._files) == 1:
b792147d 379 return _("Remove File %s") % os.path.basename((self._files[0])[1])
1f780e48
RD
380 else:
381 return _("Remove Files")
382
383
384 def Do(self):
b792147d
RD
385 status = False
386 projects = []
387 for data in self._files:
388 proj, filename = data
389 if proj not in projects:
390 projects.append(proj)
391 for project in projects:
392 files = []
393 for data in self._files:
394 proj, filename = data
395 if project == proj:
396 files.append(filename)
397 status = project.RemoveFiles(files) or status
398 return status
1f780e48
RD
399
400
401 def Undo(self):
b792147d
RD
402 status = False
403 projects = []
404 for data in self._files:
405 proj, filename = data
406 if proj not in projects:
407 projects.append(proj)
408 for project in projects:
409 files = []
410 for data in self._files:
411 proj, filename = data
412 if project == proj:
413 files.append(filename)
414 status = project.AddFiles(files) or status
415 return status
416
1f780e48
RD
417
418class ProjectRenameFileCommand(wx.lib.docview.Command):
419
b792147d 420
1f780e48
RD
421 def __init__(self, projectDoc, oldFile, newFile, isProject = False):
422 wx.lib.docview.Command.__init__(self, canUndo = True)
423 self._projectDoc = projectDoc
424 self._oldFile = oldFile
425 self._newFile = newFile
426 self._isProject = isProject
427
428
429 def GetName(self):
b792147d 430 return _("Rename File %s to %s") % (os.path.basename(self._oldFile), os.path.basename(self._newFile))
1f780e48
RD
431
432
433 def Do(self):
434 return self._projectDoc.RenameFile(self._oldFile, self._newFile, self._isProject)
435
436
437 def Undo(self):
438 return self._projectDoc.RenameFile(self._newFile, self._oldFile, self._isProject)
439
440
441class ProjectTreeCtrl(wx.TreeCtrl):
b792147d
RD
442
443
444 #----------------------------------------------------------------------------
445 # Overridden Methods
446 #----------------------------------------------------------------------------
1f780e48
RD
447
448 def __init__(self, parent, id, style):
449 wx.TreeCtrl.__init__(self, parent, id, style = style)
450
451 templates = wx.GetApp().GetDocumentManager().GetTemplates()
452 iconList = wx.ImageList(16, 16, initialCount = len(templates))
453 self._iconIndexLookup = []
454 for template in templates:
455 icon = template.GetIcon()
456 if icon:
b792147d
RD
457 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
458 icon.SetHeight(16)
459 icon.SetWidth(16)
460 if wx.GetApp().GetDebug():
461 print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
1f780e48
RD
462 iconIndex = iconList.AddIcon(icon)
463 self._iconIndexLookup.append((template, iconIndex))
464
465 icon = getBlankIcon()
b792147d
RD
466 if icon.GetHeight() != 16 or icon.GetWidth() != 16:
467 icon.SetHeight(16)
468 icon.SetWidth(16)
469 if wx.GetApp().GetDebug():
470 print "Warning: getBlankIcon isn't 16x16, not crossplatform"
1f780e48
RD
471 self._blankIconIndex = iconList.AddIcon(icon)
472 self.AssignImageList(iconList)
473
474
475 def OnCompareItems(self, item1, item2):
476 return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower())
477
478
479 def AppendItem(self, parent, filepath):
480 item = wx.TreeCtrl.AppendItem(self, parent, filepath)
481
482 found = False
483 template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filepath)
484 if not template and parent == self.GetRootItem(): # If the parent is a root it's a new project
485 template = wx.GetApp().GetDocumentManager().FindTemplateForPath('.agp')
486 if template:
487 for t, iconIndex in self._iconIndexLookup:
488 if t is template:
489 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Normal)
490 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Expanded)
491 self.SetItemImage(item, iconIndex, wx.TreeItemIcon_Selected)
492 found = True
493 break
494
495 if not found:
496 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Normal)
497 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Expanded)
498 self.SetItemImage(item, self._blankIconIndex, wx.TreeItemIcon_Selected)
499
500 return item
501
502
b792147d
RD
503 #----------------------------------------------------------------------------
504 # Client Data
505 #----------------------------------------------------------------------------
506
507 def SetData(self, item, longFilename, projectDoc=None):
508 self.SetPyData(item, (longFilename, projectDoc))
509
510
511 def GetData(self, item):
512 """ returns longFilename and optional
513 """
514 data = self.GetPyData(item)
515 if not data:
516 return (None, None)
517 return data
518
519
520 def GetLongFilename(self, item):
521 return self.GetData(item)[0]
522
523
524 def GetProjectDoc(self, item):
525 return self.GetData(item)[1]
526
527
1f780e48
RD
528class ProjectView(wx.lib.docview.View):
529
530
531 #----------------------------------------------------------------------------
532 # Overridden methods
533 #----------------------------------------------------------------------------
534
535 def __init__(self, service = None):
536 wx.lib.docview.View.__init__(self)
537 self._service = service # not used, but kept to match other Services
1f780e48
RD
538 self._treeCtrl = None
539 self._editingSoDontKillFocus = False
540 self._checkEditMenu = True
541
542
543 def Destroy(self):
544 projectService = wx.GetApp().GetService(ProjectService)
545 if projectService:
546 projectService.SetView(None)
547 wx.lib.docview.View.Destroy(self)
548
549
550 def GetDocument(self):
551 if not self._treeCtrl:
552 return None
553
554 items = self._treeCtrl.GetSelections()
555 if not items: # No selection, so just return first project
556 item = self._treeCtrl.GetFirstVisibleItem()
557 if item.IsOk():
558 return self._GetItemProject(item)
559 else:
560 return None
561
562 for item in items:
563 project = self._GetItemProject(item)
564 if project:
565 return project
566
567 return None
568
569
570 def GetDocumentManager(self): # Overshadow this since the superclass uses the view._viewDocument attribute directly, which the project editor doesn't use since it hosts multiple docs
571 return wx.GetApp().GetDocumentManager()
572
573
574 def OnChangeFilename(self):
575 if self.GetFrame():
576 title = _("Projects")
577 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName():
578 title = title + " - " + wx.GetApp().GetAppName()
579 self.GetFrame().SetTitle(title)
580 project = self.GetDocument()
581 if project:
582 projectItem = self._GetProjectItem(project)
583 name = self._treeCtrl.GetItemText(self._GetProjectItem(project))
584 name2 = self._MakeProjectName(project)
585 if name != name2:
586 self._treeCtrl.SetItemText(projectItem, name2)
587 self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem())
588
589
590 def Activate(self, activate = True):
591 if not wx.GetApp().IsMDI():
592 if activate and not self.IsShown():
593 self.Show()
594
595 if self.IsShown():
596 wx.lib.docview.View.Activate(self, activate = activate)
597 if activate and self._treeCtrl:
598 self._treeCtrl.SetFocus()
599
600
601 def OnCreate(self, doc, flags):
602 config = wx.ConfigBase_Get()
603 if wx.GetApp().IsMDI():
604 self._embeddedWindow = wx.GetApp().GetTopWindow().GetEmbeddedWindow(wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT)
605 self.SetFrame(self._embeddedWindow)
606 frame = self._embeddedWindow
607 else:
608 self._embeddedWindow = None
609 pos = config.ReadInt("ProjectFrameXLoc", -1), config.ReadInt("ProjectFrameYLoc", -1)
610 # make sure frame is visible
611 screenWidth = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X)
612 screenHeight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
613 if pos[0] < 0 or pos[0] >= screenWidth or pos[1] < 0 or pos[1] >= screenHeight:
614 pos = wx.DefaultPosition
615
616 size = wx.Size(config.ReadInt("ProjectFrameXSize", -1), config.ReadInt("ProjectFrameYSize", -1))
617
618 title = _("Projects")
619 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName():
620 title = title + " - " + wx.GetApp().GetAppName()
621
622 frame = wx.GetApp().CreateDocumentFrame(self, doc, 0, title = title, pos = pos, size = size)
623 if config.ReadInt("ProjectFrameMaximized", False):
624 frame.Maximize(True)
625
626 sizer = wx.BoxSizer()
627 self._treeCtrl = ProjectTreeCtrl(frame, -1, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.TR_DEFAULT_STYLE | wx.TR_MULTIPLE)
628 self._treeCtrl.AddRoot(_("Projects"))
b792147d
RD
629 wx.EVT_TREE_BEGIN_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginDrag)
630 wx.EVT_TREE_END_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndDrag)
1f780e48
RD
631
632 if self._embeddedWindow:
633 sizer.Add(self._treeCtrl)
634 sizer.Fit(frame)
635 else:
636 sizer.Add(self._treeCtrl, 1, wx.EXPAND, 0)
637 frame.SetSizer(sizer)
638 frame.Layout()
639 self.Activate()
640
641 if wx.GetApp().IsMDI():
642 wx.EVT_SET_FOCUS(self._treeCtrl, self.OnFocus)
643 wx.EVT_KILL_FOCUS(self._treeCtrl, self.OnKillFocus)
644
645 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
646 wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelectionSDI)
647 else:
648 wx.EVT_TREE_ITEM_ACTIVATED(self._treeCtrl, self._treeCtrl.GetId(), self.OnOpenSelection)
649 wx.EVT_TREE_BEGIN_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginLabelEdit)
650 wx.EVT_TREE_END_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndLabelEdit)
651 wx.EVT_RIGHT_DOWN(self._treeCtrl, self.OnRightClick)
652 wx.EVT_KEY_DOWN(self._treeCtrl, self.OnKeyPressed)
653 # wx.EVT_COMMAND_RIGHT_CLICK(self._treeCtrl, self._treeCtrl.GetId(), self.OnRightClick) # wxBug: This isn't working for some reason
654
655 # drag-and-drop support
656 dt = ProjectFileDropTarget(self)
657 self._treeCtrl.SetDropTarget(dt)
658
659 return True
660
661
b792147d
RD
662 def OnBeginDrag(self, event):
663 item = event.GetItem()
664 if item.IsOk():
665 if item == self._treeCtrl.GetRootItem():
666 return
667 self._draggingItem = item
668 event.Allow()
669
670
671 def OnEndDrag(self, event):
672 item = event.GetItem()
673 if item.IsOk():
674 # don't allow object to be dragged to itself
675 if item == self._draggingItem:
676 return
677
678 rootItem = self._treeCtrl.GetRootItem()
679
680 # don't let object replace root view
681 if item == rootItem:
682 wx.MessageBox(_("Cannot replace root view with item."))
683 return
684
685 # don't allow object to be dragged to a direct descendant
686 ancestor = self._treeCtrl.GetItemParent(item)
687 while ancestor != rootItem:
688 if ancestor == self._draggingItem:
689 wx.MessageBox(_("Cannot make item direct descendant of self."))
690 return
691 else:
692 ancestor = self._treeCtrl.GetItemParent(ancestor)
693
694 if self._treeCtrl.GetItemParent(item) == self._treeCtrl.GetItemParent(self._draggingItem):
695 # put it in same folder as it was originally, no-op.
696 return
697 if item == self._treeCtrl.GetItemParent(self._draggingItem):
698 # put it in same folder as it was originally, no-op.
699 return
700
701 self.GetDocument().GetCommandProcessor().Submit(ProjectEditorMoveCommand(self, item, self._draggingItem))
702
703
1f780e48
RD
704 def WriteProjectConfig(self):
705 frame = self.GetFrame()
706 config = wx.ConfigBase_Get()
707 if frame and not self._embeddedWindow:
708 if not frame.IsMaximized():
709 config.WriteInt("ProjectFrameXLoc", frame.GetPositionTuple()[0])
710 config.WriteInt("ProjectFrameYLoc", frame.GetPositionTuple()[1])
711 config.WriteInt("ProjectFrameXSize", frame.GetSizeTuple()[0])
712 config.WriteInt("ProjectFrameYSize", frame.GetSizeTuple()[1])
713 config.WriteInt("ProjectFrameMaximized", frame.IsMaximized())
714
715 if config.ReadInt("ProjectSaveDocs", True):
716 projectFileNames = []
717 projectExpanded = []
718 if self._treeCtrl:
719 for projectItem in self._GetChildItems(self._treeCtrl.GetRootItem()):
720 project = self._GetItemProject(projectItem)
721 if not project.OnSaveModified():
722 return
723 if project.GetDocumentSaved(): # Might be a new document and "No" selected to save it
724 projectFileNames.append(str(project.GetFilename()))
725 projectExpanded.append(self._treeCtrl.IsExpanded(projectItem))
726 config.Write("ProjectSavedDocs", projectFileNames.__repr__())
727 config.Write("ProjectExpandedSavedDocs", projectExpanded.__repr__())
728
729
730 def OnClose(self, deleteWindow = True):
731 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
732 self.WriteProjectConfig()
733 project = self.GetDocument()
734 if not project:
735 return True
736 if not self.GetDocument().Close():
737 return True
738 self.Activate(False)
739 if project:
740 projectItem = self._GetProjectItem(project)
741 if projectItem:
742 self._treeCtrl.Delete(projectItem)
743 # We don't need to delete the window since it is a floater/embedded
744 return True
745
746
747 def _GetParentFrame(self):
748 return wx.GetTopLevelParent(self.GetFrame())
749
750
751 def OnUpdate(self, sender = None, hint = None):
752 wx.lib.docview.View.OnUpdate(self, sender, hint)
753 if hint:
754 if hint[0] == "add":
755 projectItem = self._GetProjectItem(hint[1])
756 files = hint[2]
757 self._treeCtrl.UnselectAll()
758 self._treeCtrl.Expand(projectItem)
b792147d 759 item = None
1f780e48
RD
760 for file in files:
761 item = self._treeCtrl.AppendItem(projectItem, os.path.basename(file))
b792147d 762 self._treeCtrl.SetData(item, file)
1f780e48 763 self._treeCtrl.SelectItem(item)
b792147d 764 self._treeCtrl.EnsureVisible(item)
1f780e48 765 self._treeCtrl.SortChildren(projectItem)
b792147d
RD
766 if item:
767 self._treeCtrl.EnsureVisible(item) # need to be called after SortChildren
1f780e48
RD
768 elif hint[0] == "remove":
769 projectItem = self._GetProjectItem(hint[1])
770 files = hint[2]
b792147d 771 self._treeCtrl.UnselectAll()
1f780e48
RD
772 children = self._GetChildItems(projectItem)
773 for child in children:
774 if self._GetItemFile(child) in files:
775 self._treeCtrl.Delete(child)
776 elif hint[0] == "select":
777 projectItem = self._GetProjectItem(hint[1])
778 files = hint[2]
779 self._treeCtrl.UnselectAll()
780 children = self._GetChildItems(projectItem)
781 for child in children:
782 if self._GetItemFile(child) in files:
783 self._treeCtrl.SelectItem(child)
b792147d 784 self._treeCtrl.EnsureVisible(child)
1f780e48
RD
785 elif hint[0] == "rename":
786 projectItem = self._GetProjectItem(hint[1])
787 self._treeCtrl.SetItemText(projectItem, os.path.basename(hint[2]))
788
789
790 def ProcessEvent(self, event):
791 id = event.GetId()
792 if id == ProjectService.ADD_FILES_TO_PROJECT_ID:
793 self.OnAddFileToProject(event)
794 return True
b792147d
RD
795 elif id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID:
796 self.OnAddDirToProject(event)
797 return True
1f780e48
RD
798 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
799 return False # Implement this one in the service
800 elif id == ProjectService.RENAME_ID:
801 self.OnRename(event)
802 return True
b792147d
RD
803 elif id == ProjectService.DELETE_FILE_ID:
804 self.OnDeleteFile(event)
805 return True
1f780e48
RD
806 elif id == wx.ID_CUT:
807 self.OnCut(event)
808 return True
809 elif id == wx.ID_COPY:
810 self.OnCopy(event)
811 return True
812 elif id == wx.ID_PASTE:
813 self.OnPaste(event)
814 return True
b792147d
RD
815 elif (id == wx.ID_CLEAR
816 or id == ProjectService.REMOVE_FROM_PROJECT):
1f780e48
RD
817 self.OnClear(event)
818 return True
819 elif id == wx.ID_SELECTALL:
820 self.OnSelectAll(event)
821 return True
822 elif id == ProjectService.OPEN_SELECTION_ID:
823 self.OnOpenSelection(event)
824 return True
825 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
826 self.OnProperties(event)
827 return True
828 else:
829 return False
830
b792147d 831
1f780e48
RD
832 def ProcessUpdateUIEvent(self, event):
833 # Hack: The edit menu is not being set for projects that are preloaded at startup, so make sure it is OK here
834 if self._checkEditMenu:
835 doc = self.GetDocument()
836 if doc and not doc.GetCommandProcessor().GetEditMenu():
837 doc.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
838 self._checkEditMenu = False
b792147d 839
1f780e48 840 id = event.GetId()
b792147d
RD
841 if (id == wx.ID_CUT
842 or id == wx.ID_COPY
843 or id == ProjectService.RENAME_ID
844 or id == ProjectService.ADD_FILES_TO_PROJECT_ID
845 or id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID
26ee3a06 846 or id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID):
b792147d 847 event.Enable(self._HasSelection())
1f780e48 848 return True
26ee3a06
RD
849 elif id == ProjectService.DELETE_FILE_ID:
850 event.Enable(len(self.GetSelectedFiles()) > 0)
851 return True
1f780e48
RD
852 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
853 event.Enable(False)
854 return True
1f780e48
RD
855 elif id == wx.ID_PASTE:
856 event.Enable(self.CanPaste())
857 return True
1f780e48
RD
858 elif id == wx.ID_SELECTALL:
859 event.Enable(self._HasFiles())
860 return True
b792147d
RD
861 elif (id == wx.ID_CLEAR
862 or id == ProjectService.REMOVE_FROM_PROJECT
863 or id == ProjectService.OPEN_SELECTION_ID):
1f780e48
RD
864 event.Enable(self._HasFilesSelected())
865 return True
26ee3a06
RD
866 elif (id == wx.ID_PREVIEW
867 or id == wx.ID_PRINT):
868 event.Enable(False)
869 return True
1f780e48
RD
870 else:
871 return False
872
873 #----------------------------------------------------------------------------
874 # Display Methods
875 #----------------------------------------------------------------------------
876
877 def IsShown(self):
878 if not self.GetFrame():
879 return False
880 return self.GetFrame().IsShown()
881
882
883 def Hide(self):
884 self.Show(False)
885
886
887 def Show(self, show = True):
888 self.GetFrame().Show(show)
889 if wx.GetApp().IsMDI():
890 mdiParentFrame = wx.GetApp().GetTopWindow()
891 mdiParentFrame.ShowEmbeddedWindow(self.GetFrame(), show)
892
893
894 #----------------------------------------------------------------------------
895 # Methods for ProjectDocument and ProjectService to call
896 #----------------------------------------------------------------------------
897
898 def SetExpandedProjects(self, expandedProjects):
899 self._treeCtrl.UnselectAll()
900 firstItem = None
901 for i, item in enumerate(self._GetChildItems(self._treeCtrl.GetRootItem())):
902 if i == 0:
903 firstItem = item
bbf7159c 904
1f780e48
RD
905 if expandedProjects[i]:
906 self._treeCtrl.Expand(item)
907 else:
908 self._treeCtrl.Collapse(item)
bbf7159c 909
1f780e48 910 if firstItem:
bbf7159c
RD
911 self._treeCtrl.EnsureVisible(firstItem)
912
1f780e48
RD
913
914 def GetSelectedFile(self):
915 for item in self._treeCtrl.GetSelections():
916 return self._GetItemFile(item)
6f1a3f9c 917
1f780e48 918
6f1a3f9c
RD
919 def GetSelectedFiles(self):
920 filenames = []
921 for item in self._treeCtrl.GetSelections():
922 filename = self._GetItemFile(item)
923 if filename and filename not in filenames:
924 filenames.append(filename)
925 return filenames
926
bbf7159c 927
26ee3a06
RD
928 def GetSelectedProjects(self):
929 filenames = []
930 for item in self._treeCtrl.GetSelections():
931 if self._IsItemProject(item):
932 filename = self._treeCtrl.GetLongFilename(item)
933 if filename and filename not in filenames:
934 filenames.append(filename)
935 return filenames
936
937
1f780e48
RD
938 def AddProjectToView(self, document):
939 rootItem = self._treeCtrl.GetRootItem()
940 projectItem = self._treeCtrl.AppendItem(rootItem, self._MakeProjectName(document))
b792147d 941 self._treeCtrl.SetData(projectItem, document.GetFilename(), document)
1f780e48
RD
942 for file in document.GetFiles():
943 fileItem = self._treeCtrl.AppendItem(projectItem, os.path.basename(file))
b792147d 944 self._treeCtrl.SetData(fileItem, file)
1f780e48
RD
945 self._treeCtrl.SortChildren(rootItem)
946 self._treeCtrl.SortChildren(projectItem)
947 self._treeCtrl.UnselectAll()
948 self._treeCtrl.Expand(projectItem)
949 self._treeCtrl.SelectItem(projectItem)
950 if self._embeddedWindow:
951 document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
952
1f780e48 953
6f1a3f9c
RD
954 def HasFocus(self):
955 winWithFocus = wx.Window.FindFocus()
956 if not winWithFocus:
957 return False
958 while winWithFocus:
959 if winWithFocus == self._treeCtrl:
960 return True
961 winWithFocus = winWithFocus.GetParent()
962 return False
963
964
1f780e48
RD
965 #----------------------------------------------------------------------------
966 # Control events
967 #----------------------------------------------------------------------------
968
969 def OnProperties(self, event):
970 items = self._treeCtrl.GetSelections()
971 if not items:
972 return
973 item = items[0]
974 if self._IsItemProject(item):
975 projectPropertiesDialog = ProjectPropertiesDialog(wx.GetApp().GetTopWindow(), self._GetItemProject(item).GetFilename())
976 if projectPropertiesDialog.ShowModal() == wx.ID_OK:
977 pass # Handle OK
978 projectPropertiesDialog.Destroy()
979 elif self._IsItemFile(item):
980 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
981 filePropertiesService.ShowPropertiesDialog(self._GetItemFile(item))
982
983
984 def OnAddFileToProject(self, event):
985 if wx.Platform == "__WXMSW__" or wx.Platform == "__WXGTK__" or wx.Platform == "__WXMAC__":
986 allfilter = ''
987 descr = ''
988 for temp in self.GetDocumentManager()._templates:
989 if temp.IsVisible():
990 if len(descr) > 0:
991 descr = descr + _('|')
992 allfilter = allfilter + _(';')
993 descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter() # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
994 allfilter = allfilter + temp.GetFileFilter()
26ee3a06 995 descr = _("All (%s)|%s|%s|Any (*.*) | *.*") % (allfilter, allfilter, descr) # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
1f780e48
RD
996 else:
997 descr = _("*.*")
b792147d
RD
998
999 startDirectory = os.path.dirname(self.GetDocument().GetFilename())
1000
1f780e48 1001 if True or _WINDOWS:
b792147d 1002 dialog = wx.FileDialog(self.GetFrame(), _("Add Files"), startDirectory, "", descr, wx.OPEN | wx.HIDE_READONLY | wx.MULTIPLE)
1f780e48
RD
1003 if dialog.ShowModal() != wx.ID_OK:
1004 return
1005 paths = dialog.GetPaths()
1006 dialog.Destroy()
1007 else:
b792147d 1008 paths = wx.FileSelector(_("Add Files"), startDirectory, "", wildcard = descr, flags = wx.OPEN | wx.HIDE_READONLY | wx.MULTIPLE, parent=self.GetFrame())
1f780e48
RD
1009 if type(paths) == types.StringType:
1010 paths = [paths]
1011 if len(paths):
1f780e48
RD
1012 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), paths))
1013 self.Activate(True) # after add, should put focus on project editor
1014
1015
b792147d 1016 def OnAddDirToProject(self, event):
6f1a3f9c 1017 frame = wx.Dialog(None, -1, _("Add Directory Files to Project"), size= (320,200))
b792147d 1018 contentSizer = wx.BoxSizer(wx.VERTICAL)
bbb28897
RD
1019
1020 flexGridSizer = wx.FlexGridSizer(cols = 2, vgap=HALF_SPACE, hgap=HALF_SPACE)
1021 flexGridSizer.Add(wx.StaticText(frame, -1, _("Directory:")), 0, wx.ALIGN_CENTER_VERTICAL, 0)
b792147d 1022 lineSizer = wx.BoxSizer(wx.HORIZONTAL)
bbb28897 1023 dirCtrl = wx.TextCtrl(frame, -1, os.path.dirname(self.GetDocument().GetFilename()), size=(250,-1))
b792147d 1024 dirCtrl.SetToolTipString(dirCtrl.GetValue())
bbb28897 1025 lineSizer.Add(dirCtrl, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
6f1a3f9c 1026 findDirButton = wx.Button(frame, -1, _("Browse..."))
bbb28897
RD
1027 lineSizer.Add(findDirButton, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, HALF_SPACE)
1028 flexGridSizer.Add(lineSizer, 1, wx.EXPAND)
1029
b792147d
RD
1030 def OnBrowseButton(event):
1031 dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
1032 dir = dirCtrl.GetValue()
1033 if len(dir):
1034 dlg.SetPath(dir)
1035 if dlg.ShowModal() == wx.ID_OK:
1036 dirCtrl.SetValue(dlg.GetPath())
1037 dirCtrl.SetToolTipString(dirCtrl.GetValue())
1038 dirCtrl.SetInsertionPointEnd()
1039
1040 dlg.Destroy()
1041 wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
1042
1043 visibleTemplates = []
1044 for template in self.GetDocumentManager()._templates:
1045 if template.IsVisible():
1046 visibleTemplates.append(template)
1047
1048 choices = []
1049 allfilter = ''
1050 descr = ''
1051 for template in visibleTemplates:
1052 if len(descr) > 0:
1053 descr = descr + _('|')
1054 allfilter = allfilter + _(';')
1055 descr = template.GetDescription() + _(" (") + template.GetFileFilter() + _(")")
1056 choices.append(descr)
1057 allfilter = allfilter + template.GetFileFilter()
1058 choices.insert(0, _("All (%s)") % allfilter)
bbb28897 1059 filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices)
b792147d
RD
1060 filterChoice.SetSelection(0)
1061 filterChoice.SetToolTipString(_("Select file type filter."))
bbb28897
RD
1062 flexGridSizer.Add(wx.StaticText(frame, -1, _("Files of type:")), 0, wx.ALIGN_CENTER_VERTICAL)
1063 flexGridSizer.Add(filterChoice, 1, wx.EXPAND)
1064
1065 contentSizer.Add(flexGridSizer, 0, wx.ALL|wx.EXPAND, SPACE)
b792147d
RD
1066
1067 subfolderCtrl = wx.CheckBox(frame, -1, _("Add files from subdirectories"))
1068 subfolderCtrl.SetValue(True)
bbb28897 1069 contentSizer.Add(subfolderCtrl, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, SPACE)
b792147d 1070
bbb28897 1071 buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
b792147d
RD
1072 findBtn = wx.Button(frame, wx.ID_OK, _("Add"))
1073 findBtn.SetDefault()
bbb28897 1074 buttonSizer.Add(findBtn, 0, wx.RIGHT, HALF_SPACE)
b792147d 1075 buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
bbb28897 1076 contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
b792147d 1077
bbb28897 1078 frame.SetSizer(contentSizer)
b792147d
RD
1079 frame.Fit()
1080
1081 status = frame.ShowModal()
1082
1083 passedCheck = False
1084 while status == wx.ID_OK and not passedCheck:
1085 if not os.path.exists(dirCtrl.GetValue()):
1086 dlg = wx.MessageDialog(frame,
1087 _("'%s' does not exist.") % dirCtrl.GetValue(),
1088 _("Find in Directory"),
1089 wx.OK | wx.ICON_EXCLAMATION
1090 )
1091 dlg.ShowModal()
1092 dlg.Destroy()
1093
1094 status = frame.ShowModal()
1095 else:
1096 passedCheck = True
1097
1098 if status == wx.ID_OK:
1099 frame.Destroy()
1100
1101 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
1102
1103 doc = self.GetDocument()
1104 searchSubfolders = subfolderCtrl.IsChecked()
1105 dirString = dirCtrl.GetValue()
1106
1107 if os.path.isfile(dirString):
1108 # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
1109 # We'll assume they know what they're doing.
1110 paths = [dirString]
1111 else:
1112 paths = []
1113
1114 index = filterChoice.GetSelection()
1115 if index:
1116 template = visibleTemplates[index-1]
1117
1118 # do search in files on disk
1119 for root, dirs, files in os.walk(dirString):
1120 if not searchSubfolders and root != dirString:
1121 break
1122
1123 for name in files:
1124 if index == 0: # all
1125 for template in visibleTemplates:
1126 if template.FileMatchesTemplate(name):
1127 filename = os.path.join(root, name)
1128
1129 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
1130 if doc.IsFileInProject(filename):
1131 break
1132
1133 paths.append(filename)
1134 break
1135 else: # use selected filter
1136 if template.FileMatchesTemplate(name):
1137 filename = os.path.join(root, name)
1138 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
1139 if not doc.IsFileInProject(filename):
1140 paths.append(filename)
1141
1142 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
1143
1144 doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths))
1145 self.Activate(True) # after add, should put focus on project editor
1146 else:
1147 frame.Destroy()
1148
1149
1f780e48
RD
1150 def DoAddFilesToProject(self, filenames):
1151 # method used by Drag-n-Drop to add files to current Project
1152 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), filenames))
1153
1154
1155 def DoSelectFiles(self, filenames):
1156 # method used by Drag-n-Drop to select files in current Project
b792147d 1157 self._treeCtrl.UnselectAll()
1f780e48
RD
1158 for file in filenames:
1159 item = self._GetFileItem(longFileName=file)
1160 if item:
1161 self._treeCtrl.SelectItem(item, True)
1162 self._treeCtrl.EnsureVisible(item)
1163
1164
1165 def DoSelectProject(self, x, y):
1166 # method used by Drag-n-Drop to set current Project based on cursor position
1167 item, flag = self._treeCtrl.HitTest((x,y))
1168 if not item:
1169 return False
1170
1171 project = self._GetItemProject(item)
1172 if not project:
1173 return False
1174
1175 projectItem = self._GetProjectItem(project)
1176 self._treeCtrl.UnselectAll()
1177 self._treeCtrl.SelectItem(projectItem)
1178 return True
1179
1180
1181 def OnFocus(self, event):
1182 wx.GetApp().GetDocumentManager().ActivateView(self)
1183 event.Skip()
1184
1185
1186 def OnKillFocus(self, event):
1187 # Get the top MDI window and "activate" it since it is already active from the perspective of the MDIParentFrame
1188 # wxBug: Would be preferable to call OnActivate, but have casting problem, so added Activate method to docview.DocMDIChildFrame
1189 if not self._editingSoDontKillFocus: # wxBug: This didn't used to happen, but now when you start to edit an item in a wxTreeCtrl it puts out a KILL_FOCUS event, so we need to detect it
1190 childFrame = wx.GetApp().GetTopWindow().GetActiveChild()
1191 if childFrame:
1192 childFrame.Activate()
1193 event.Skip()
1194
1195
1196 def OnRightClick(self, event):
1197 self.Activate(True)
1198 if not self._treeCtrl.GetSelections():
1199 return
1200 if len(self._treeCtrl.GetSelections()) == 1 and self._IsItemRoot(self._treeCtrl.GetSelections()[0]):
1201 return # Don't do a menu if it's just the root item selected
1202 menu = wx.Menu()
1203 if self._HasFilesSelected(): # Files context
1204 menu.Append(ProjectService.OPEN_SELECTION_ID, _("&Open"), _("Opens the selection"))
1205 menu.Enable(ProjectService.OPEN_SELECTION_ID, True)
1206 wx.EVT_MENU(self._GetParentFrame(), ProjectService.OPEN_SELECTION_ID, self.OnOpenSelection)
1207 itemIDs = [None]
1208 for item in self._treeCtrl.GetSelections():
1209 if self._IsItemProcessModelFile(item):
1210 itemIDs = [None, ProjectService.RUN_SELECTED_PM_ID, None]
1211 break
1212 else: # Project context
1213 itemIDs = [wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS, None]
1214 menuBar = self._GetParentFrame().GetMenuBar()
6f1a3f9c 1215 itemIDs = itemIDs + [ProjectService.ADD_FILES_TO_PROJECT_ID, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, ProjectService.REMOVE_FROM_PROJECT]
26ee3a06 1216 svnIDs = [SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID]
6f1a3f9c
RD
1217 if SVN_INSTALLED:
1218 itemIDs = itemIDs + [None, SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID]
26ee3a06 1219 globalIDs = [wx.ID_UNDO, wx.ID_REDO, wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS]
6f1a3f9c 1220 itemIDs = itemIDs + [None, wx.ID_UNDO, wx.ID_REDO, None, wx.ID_CUT, wx.ID_COPY, wx.ID_PASTE, wx.ID_CLEAR, None, wx.ID_SELECTALL, ProjectService.RENAME_ID, ProjectService.DELETE_FILE_ID, None, wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID]
1f780e48
RD
1221 for itemID in itemIDs:
1222 if not itemID:
1223 menu.AppendSeparator()
1224 else:
1225 if itemID == ProjectService.RUN_SELECTED_PM_ID:
1226 menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
1227 wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.OnRunSelectedPM)
1228 elif itemID == ProjectService.REMOVE_FROM_PROJECT:
1229 menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project"))
1230 wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear)
1231 wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent)
1232 else:
26ee3a06 1233 svnService = wx.GetApp().GetService(SVNService.SVNService)
1f780e48
RD
1234 item = menuBar.FindItemById(itemID)
1235 if item:
26ee3a06
RD
1236 if itemID in svnIDs:
1237 if SVN_INSTALLED and svnService:
1238 wx.EVT_MENU(self._GetParentFrame(), itemID, svnService.ProcessEvent)
1239 elif itemID in globalIDs:
1240 pass
1241 else:
1242 wx.EVT_MENU(self._treeCtrl, itemID, self.ProcessEvent)
1f780e48
RD
1243 menu.Append(itemID, item.GetLabel())
1244 self._treeCtrl.PopupMenu(menu, wx.Point(event.GetX(), event.GetY()))
1245 menu.Destroy()
1246
b792147d 1247
1f780e48
RD
1248 def OnRunSelectedPM(self, event):
1249 projectService = wx.GetApp().GetService(ProjectService)
b792147d
RD
1250 if projectService:
1251 projectService.OnRunProcessModel(event, runSelected=True)
1252
1f780e48
RD
1253
1254 def OnRename(self, event):
1255 if self._treeCtrl.GetSelections():
1256 self._treeCtrl.EditLabel(self._treeCtrl.GetSelections()[0])
1257
1258
1259 def OnBeginLabelEdit(self, event):
1260 self._editingSoDontKillFocus = True
1261 item = event.GetItem()
1262 if not self._IsItemFile(item) and not self._IsItemProject(item):
1263 event.Veto()
1264
1265
1266 def OnEndLabelEdit(self, event):
1267 self._editingSoDontKillFocus = False
1268 item = event.GetItem()
1269 newName = event.GetLabel()
1270 if not newName or (not self._IsItemFile(item) and not self._IsItemProject(item)):
1271 event.Veto()
1272 return
1273 if self._IsItemFile(item):
1274 oldFile = self._GetItemFile(item)
1275 newFile = os.path.join(os.path.split(oldFile)[0], newName)
b792147d
RD
1276 project = self._GetItemProject(item)
1277 if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile)):
1f780e48
RD
1278 event.Veto()
1279 return
1280 self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(self._treeCtrl.GetSelections()[0]))
1281 elif self._IsItemProject(item):
1282 oldFile = self._GetItemProject(item).GetFilename()
1283 newFile = os.path.join(os.path.split(oldFile)[0], newName)
b792147d
RD
1284 project = self._GetItemProject(item)
1285 if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile, True)):
1f780e48
RD
1286 event.Veto()
1287 return
1288 self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem())
1289
1290
1291 def CanPaste(self):
1292 # wxBug: Should be able to use IsSupported/IsSupportedFormat here
1293 #fileDataObject = wx.FileDataObject()
1294 #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject)
1295 if not wx.TheClipboard.IsOpened():
1296 if wx.TheClipboard.Open():
1297 fileDataObject = wx.FileDataObject()
1298 hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject)
1299 wx.TheClipboard.Close()
1300 else:
1301 hasFilesInClipboard = False
1302 return hasFilesInClipboard
1303
1304
1305 def OnCut(self, event):
b792147d
RD
1306 self.OnCopy(event)
1307 self.OnClear(event)
1f780e48
RD
1308
1309
1310 def OnCopy(self, event):
1311 fileDataObject = wx.FileDataObject()
1312 items = self._treeCtrl.GetSelections()
1313 for item in items:
1314 if self._IsItemFile(item):
b792147d 1315 file = self._treeCtrl.GetLongFilename(item)
1f780e48
RD
1316 fileDataObject.AddFile(file)
1317 if len(fileDataObject.GetFilenames()) > 0 and wx.TheClipboard.Open():
1318 wx.TheClipboard.SetData(fileDataObject)
1319 wx.TheClipboard.Close()
1320
1321
1322 def OnPaste(self, event):
1323 if wx.TheClipboard.Open():
1324 fileDataObject = wx.FileDataObject()
1325 if wx.TheClipboard.GetData(fileDataObject):
1326 self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), fileDataObject.GetFilenames()))
1327 wx.TheClipboard.Close()
1328
1329
1330 def OnClear(self, event):
b792147d
RD
1331 items = self._treeCtrl.GetSelections()
1332 files = []
1333 for item in items:
1334 if self._IsItemFile(item):
1335 files.append((self._GetItemProject(item), self._GetItemFile(item)))
1336 self.GetDocument().GetCommandProcessor().Submit(ProjectRemoveFilesCommand(files))
1f780e48
RD
1337
1338
b792147d
RD
1339 def OnDeleteFile(self, event):
1340 yesNoMsg = wx.MessageDialog(self.GetFrame(),
1341 _("Delete cannot be reversed.\n\nRemove the selected files from the\nprojects and file system permanently?"),
1342 _("Delete File"),
1343 wx.YES_NO|wx.ICON_QUESTION)
1344 if yesNoMsg.ShowModal() == wx.ID_NO:
1345 return
1346
1347 items = self._treeCtrl.GetSelections()
1348 files = []
1349 delFiles = []
1350 for item in items:
1351 if self._IsItemFile(item):
1352 filename = self._GetItemFile(item)
1353 files.append((self._GetItemProject(item), filename))
1354 if filename not in delFiles:
1355 delFiles.append(filename)
1356
1357 # remove selected files from projects
1358 projects = []
1359 for data in files:
1360 proj, filename = data
1361 if proj not in projects:
1362 projects.append(proj)
1363 for project in projects:
1364 filenames = []
1365 for data in files:
1366 proj, filename = data
1367 if project == proj:
1368 filenames.append(filename)
1369 project.RemoveFiles(filenames)
1370
1371 # remove selected files from file system
1372 for filename in delFiles:
1373 if os.path.exists(filename):
1374 try:
1375 os.remove(filename)
1376 except:
1377 wx.MessageBox("Could not delete '%s'. %s" % (os.path.basename(filename), sys.exc_value),
1378 _("Delete File"),
1379 wx.OK | wx.ICON_EXCLAMATION)
1380
1381
1f780e48
RD
1382 def OnKeyPressed(self, event):
1383 key = event.KeyCode()
1384 if key == wx.WXK_DELETE:
1385 self.OnClear(event)
1386 else:
1387 event.Skip()
1388
1389
1390 def OnSelectAll(self, event):
1391 project = self.GetDocument()
1392 if project:
1393 self._treeCtrl.UnselectAll()
1394 for child in self._GetChildItems(self._GetProjectItem(project)):
1395 self._treeCtrl.SelectItem(child)
1396
1397
1398 def OnOpenSelectionSDI(self, event):
1399 # Do a call after so that the second mouseclick on a doubleclick doesn't reselect the project window
1400 wx.CallAfter(self.OnOpenSelection, None)
1401
1402
1403 def OnOpenSelection(self, event):
1404 doc = None
1405 try:
1406 items = self._treeCtrl.GetSelections()
1407 for item in items:
1408 if self._IsItemFile(item):
1409 filepath = self._GetItemFile(item)
1410 if not os.path.exists(filepath):
1411 msgTitle = wx.GetApp().GetAppName()
1412 if not msgTitle:
1413 msgTitle = _("File Not Found")
1414 yesNoMsg = wx.MessageDialog(self.GetFrame(),
1415 _("The file '%s' was not found in '%s'.\n\nWould you like to browse for the file?") % (wx.lib.docview.FileNameFromPath(filepath), wx.lib.docview.PathOnly(filepath)),
1416 msgTitle,
b792147d 1417 wx.YES_NO|wx.ICON_QUESTION
1f780e48
RD
1418 )
1419 if yesNoMsg.ShowModal() == wx.ID_NO:
1420 continue
1421 findFile = wx.FileDialog(self.GetFrame(),
1422 _("Choose a file"),
1423 wx.lib.docview.PathOnly(filepath),
1424 wx.lib.docview.FileNameFromPath(filepath),
1425 style = wx.OPEN
1426 )
1427 if findFile.ShowModal() == wx.ID_OK and findFile.GetPath():
1428 newpath = findFile.GetPath()
1429 else:
1430 newpath = None
1431 findFile.Destroy()
1432 if newpath:
1433 # update Project Model with new location
6f1a3f9c
RD
1434 project = self._GetItemProject(item)
1435 project.RemoveFile(filepath)
1436 project.AddFile(newpath)
1f780e48
RD
1437 filepath = newpath
1438
1439 doc = self.GetDocumentManager().CreateDocument(filepath, wx.lib.docview.DOC_SILENT)
b792147d
RD
1440 if not doc:
1441 shortFilename = self._treeCtrl.GetItemText(item)
1442 if shortFilename.endswith(".agp"):
1443 projItem = self._GetProjectItem(shortFilename=shortFilename)
1444 self._treeCtrl.UnselectAll()
1445 if not self._treeCtrl.IsExpanded(projItem):
1446 self._treeCtrl.Expand(projItem)
1447 if not self._treeCtrl.IsVisible(projItem):
1448 self._treeCtrl.EnsureVisible(projItem)
1449 if not self._treeCtrl.IsSelected(projItem):
1450 self._treeCtrl.SelectItem(projItem)
1f780e48
RD
1451
1452 except IOError, (code, message):
1453 msgTitle = wx.GetApp().GetAppName()
1454 if not msgTitle:
1455 msgTitle = _("File Error")
1456 wx.MessageBox("Could not open '%s'." % wx.lib.docview.FileNameFromPath(filepath),
1457 msgTitle,
1458 wx.OK | wx.ICON_EXCLAMATION,
1459 self.GetFrame())
1460
1461
1462 #----------------------------------------------------------------------------
1463 # Convenience methods
1464 #----------------------------------------------------------------------------
1465
1466 def _HasFiles(self):
1467 if not self._treeCtrl:
1468 return False
1469 return self._treeCtrl.GetCount() > 1 # 1 item = root item, don't count as having files
1470
1471
b792147d 1472 def _HasSelection(self):
1f780e48
RD
1473 if not self._treeCtrl:
1474 return False
b792147d 1475
1f780e48 1476 items = self._treeCtrl.GetSelections()
b792147d
RD
1477 if items:
1478 return True
1479
1f780e48
RD
1480 return False
1481
1482
1483 def _HasFilesSelected(self):
1484 if not self._treeCtrl:
1485 return False
1486 items = self._treeCtrl.GetSelections()
1487 if not items:
1488 return False
1489 for item in items:
1490 if not self._IsItemFile(item):
1491 return False
1492 return True
1493
1494
1495 def _MakeProjectName(self, project):
1496 return project.GetPrintableName()
1497
1498
1499 # Return the tree item for a project
b792147d
RD
1500 def _GetProjectItem(self, project=None, shortFilename=None):
1501 rootItem = self._treeCtrl.GetRootItem()
1502 (child, cookie) = self._treeCtrl.GetFirstChild(rootItem)
1503 while child.IsOk():
1504 if project:
1505 if self._treeCtrl.GetProjectDoc(child) == project:
1506 return child
1507 elif shortFilename:
1508 if self._treeCtrl.GetItemText(child) == shortFilename:
1509 return child
1510 (child, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie)
1f780e48
RD
1511 return None
1512
1513
1514 # Returns the project for an item, either for a project item or a file that is part of a project
1515 def _GetItemProject(self, item):
1516 if self._IsItemRoot(item):
1517 return None
1518 if self._IsItemProject(item):
b792147d 1519 return self._treeCtrl.GetProjectDoc(item)
1f780e48 1520 if self._IsItemFile(item):
b792147d 1521 return self._treeCtrl.GetProjectDoc(self._treeCtrl.GetItemParent(item))
1f780e48
RD
1522 return None
1523
1524
1525 def _GetItemFile(self, item):
1526 if self._IsItemFile(item):
b792147d 1527 return self._treeCtrl.GetLongFilename(item)
1f780e48
RD
1528 else:
1529 return None
1530
1531
1532 def _GetFileItem(self, shortFileName = None, longFileName = None):
1533 """ Returns the tree item for a file given the short (display) or long (fullpath) file name. """
1534
b792147d
RD
1535 rootItem = self._treeCtrl.GetRootItem()
1536 (project, cookie) = self._treeCtrl.GetFirstChild(rootItem)
1537 while project.IsOk():
1538 (child, cookie2) = self._treeCtrl.GetFirstChild(project)
1539 while child.IsOk():
1540 if shortFileName:
1541 if self._treeCtrl.GetItemText(child) == shortFileName:
1542 return child
1543 else:
1544 if self._treeCtrl.GetLongFilename(child) == longFileName:
1545 return child
bbb28897 1546 (child, cookie2) = self._treeCtrl.GetNextChild(project, cookie2)
b792147d
RD
1547 (project, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie)
1548 return None
1f780e48
RD
1549
1550
1551 def _IsItemRoot(self, item):
1552 return item == self._treeCtrl.GetRootItem()
1553
1554
1555 def _IsItemProject(self, item):
b792147d 1556 return self._treeCtrl.GetProjectDoc(item) != None
1f780e48
RD
1557
1558
1559 def _IsItemFile(self, item):
b792147d 1560 return self._treeCtrl.GetProjectDoc(item) == None
1f780e48
RD
1561
1562
1563 def _IsItemProcessModelFile(self, item):
1564 if ACTIVEGRID_BASE_IDE:
1565 return False
1566
b792147d
RD
1567 if self._IsItemFile(item):
1568 filename = self._treeCtrl.GetLongFilename(item)
1f780e48
RD
1569 ext = None
1570 for template in self.GetDocumentManager().GetTemplates():
1571 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1572 ext = template.GetDefaultExtension()
1573 break;
1574 if not ext:
1575 return False
1576
1577 if filename.endswith(ext):
1578 return True
1579
1580 return False
1581
1582
1f780e48
RD
1583 def _GetChildItems(self, parentItem):
1584 children = []
1585 (child, cookie) = self._treeCtrl.GetFirstChild(parentItem)
1586 while child.IsOk():
1587 children.append(child)
1588 (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie)
1589 return children
1590
1591
1592
1593class ProjectFileDropTarget(wx.FileDropTarget):
1594
1595 def __init__(self, view):
1596 wx.FileDropTarget.__init__(self)
1597 self._view = view
1598
1599
1600 def OnDropFiles(self, x, y, filenames):
1601 if self._view.DoSelectProject(x, y):
1602 self._view.DoAddFilesToProject(filenames)
1603 self._view.DoSelectFiles(filenames)
1604 return True
1605 return False
1606
1607
1608 def OnDragOver(self, x, y, default):
1609 if self._view.DoSelectProject(x,y):
1610 return wx.DragCopy
1611 return wx.DragNone
1612
1613
1614class ProjectPropertiesDialog(wx.Dialog):
1615
1616
1617 def __init__(self, parent, filename):
1618 wx.Dialog.__init__(self, parent, -1, _("Project Properties"), size = (310, 330))
1619
1f780e48
RD
1620 filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService)
1621
1622 notebook = wx.Notebook(self, -1)
1623 tab = wx.Panel(notebook, -1)
1624
1625 gridSizer = RowColSizer()
1626
1627 gridSizer.Add(wx.StaticText(tab, -1, _("Filename:")), flag=wx.RIGHT, border=HALF_SPACE, row=0, col=0)
1628 if os.path.isfile(filename):
1629 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1]), row=0, col=1)
1630
1631 gridSizer.Add(wx.StaticText(tab, -1, _("Location:")), flag=wx.RIGHT, border=HALF_SPACE, row=1, col=0)
1632 gridSizer.Add(wx.StaticText(tab, -1, filePropertiesService.chopPath(os.path.split(filename)[0])), flag=wx.BOTTOM, border=SPACE, row=1, col=1)
1633
1634 gridSizer.Add(wx.StaticText(tab, -1, _("Size:")), flag=wx.RIGHT, border=HALF_SPACE, row=2, col=0)
1635 gridSizer.Add(wx.StaticText(tab, -1, str(os.path.getsize(filename)) + ' ' + _("bytes")), row=2, col=1)
1636
1637 lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
1638 lineSizer.Add(wx.StaticLine(tab, -1, size = (10,-1)), 0, wx.EXPAND)
1639 gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP, border=HALF_SPACE, row=3, col=0, colspan=2)
1640
1641 gridSizer.Add(wx.StaticText(tab, -1, _("Created:")), flag=wx.RIGHT, border=HALF_SPACE, row=4, col=0)
1642 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getctime(filename))), row=4, col=1)
1643
1644 gridSizer.Add(wx.StaticText(tab, -1, _("Modified:")), flag=wx.RIGHT, border=HALF_SPACE, row=5, col=0)
1645 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getmtime(filename))), row=5, col=1)
1646
1647 gridSizer.Add(wx.StaticText(tab, -1, _("Accessed:")), flag=wx.RIGHT, border=HALF_SPACE, row=6, col=0)
1648 gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getatime(filename))), row=6, col=1)
1649
1650 else:
1651 gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1] + ' ' + _("[new project]")), row=0, col=1)
1652
1653 # add a border around the inside of the tab
1654 spacerGrid = wx.BoxSizer(wx.VERTICAL)
1655 spacerGrid.Add(gridSizer, 0, wx.ALL, SPACE);
1656 tab.SetSizer(spacerGrid)
1657 notebook.AddPage(tab, _("General"))
1658 if wx.Platform == "__WXMSW__":
1659 notebook.SetPageSize((310,200))
1660
1661 sizer = wx.BoxSizer(wx.VERTICAL)
1662 sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
1663 sizer.Add(self.CreateButtonSizer(wx.OK), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE)
1664
1f780e48 1665 self.SetSizer(sizer)
6f1a3f9c 1666 sizer.Fit(self)
1f780e48
RD
1667 self.Layout()
1668
1669
1670class ProjectOptionsPanel(wx.Panel):
1671
1672
1673 def __init__(self, parent, id):
1674 wx.Panel.__init__(self, parent, id)
1675 self._useSashMessageShown = False
1f780e48
RD
1676 config = wx.ConfigBase_Get()
1677 self._projSaveDocsCheckBox = wx.CheckBox(self, -1, _("Remember open projects"))
1678 self._projSaveDocsCheckBox.SetValue(config.ReadInt("ProjectSaveDocs", True))
1679 projectBorderSizer = wx.BoxSizer(wx.VERTICAL)
1680 projectSizer = wx.BoxSizer(wx.VERTICAL)
1681 projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE)
bbf7159c
RD
1682 if not ACTIVEGRID_BASE_IDE:
1683 self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
1684 self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog", True))
1685 projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE)
1f780e48
RD
1686 projectBorderSizer.Add(projectSizer, 0, wx.ALL, SPACE)
1687 self.SetSizer(projectBorderSizer)
1688 self.Layout()
1689 parent.AddPage(self, _("Project"))
1690
1691 def OnUseSashSelect(self, event):
1692 if not self._useSashMessageShown:
1693 msgTitle = wx.GetApp().GetAppName()
1694 if not msgTitle:
1695 msgTitle = _("Document Options")
1696 wx.MessageBox("Project window embedded mode changes will not appear until the application is restarted.",
1697 msgTitle,
1698 wx.OK | wx.ICON_INFORMATION,
1699 self.GetParent())
1700 self._useSashMessageShown = True
1701
1702
1703 def OnOK(self, optionsDialog):
1704 config = wx.ConfigBase_Get()
1705 config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue())
bbf7159c
RD
1706 if not ACTIVEGRID_BASE_IDE:
1707 config.WriteInt("RunWelcomeDialog", self._projShowWelcomeCheckBox.GetValue())
1f780e48
RD
1708
1709
1710class ProjectService(Service.Service):
1711
1712 #----------------------------------------------------------------------------
1713 # Constants
1714 #----------------------------------------------------------------------------
1715 SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
1716 RUNPM_ID = wx.NewId()
1717 RUN_SELECTED_PM_ID = wx.NewId()
1718 RUN_CURRENT_PM_ID = wx.NewId()
1719 ADD_FILES_TO_PROJECT_ID = wx.NewId()
1720 ADD_CURRENT_FILE_TO_PROJECT_ID = wx.NewId()
1721 RENAME_ID = wx.NewId()
1722 OPEN_SELECTION_ID = wx.NewId()
1723 REMOVE_FROM_PROJECT = wx.NewId()
b792147d
RD
1724 DELETE_FILE_ID = wx.NewId()
1725 ADD_ALL_FILES_TO_PROJECT_ID = wx.NewId()
1726
1f780e48
RD
1727
1728 #----------------------------------------------------------------------------
1729 # Overridden methods
1730 #----------------------------------------------------------------------------
1731
1732 def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_LEFT):
1733 Service.Service.__init__(self, serviceName, embeddedWindowLocation)
1734 self._runHandlers = []
1735 self._suppressOpenProjectMessages = False
1736
1737
1738 def _CreateView(self):
1739 return ProjectView(self)
1740
1741
1742 def ShowWindow(self, show = True):
1743 """ Force showing of saved projects on opening, otherwise empty Project Window is disconcerting for user """
1744 Service.Service.ShowWindow(self, show)
1745
1746 if show:
1747 project = self.GetView().GetDocument()
1748 if not project:
1749 self.OpenSavedProjects()
1750
1751
1752 #----------------------------------------------------------------------------
1753 # Service specific methods
1754 #----------------------------------------------------------------------------
1755
1756 def GetSuppressOpenProjectMessages(self):
1757 return self._suppressOpenProjectMessages
1758
1759
1760 def SetSuppressOpenProjectMessages(self, suppressOpenProjectMessages):
1761 self._suppressOpenProjectMessages = suppressOpenProjectMessages
1762
1763
1764 def GetRunHandlers(self):
1765 return self._runHandlers
1766
1767
1768 def AddRunHandler(self, runHandler):
1769 self._runHandlers.append(runHandler)
1770
1771
1772 def RemoveRunHandler(self, runHandler):
1773 self._runHandlers.remove(runHandler)
1774
1775
1776 def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
1777 Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
1778
1f780e48
RD
1779 projectMenu = wx.Menu()
1780
1781## accelTable = wx.AcceleratorTable([
1782## eval(_("wx.ACCEL_CTRL, ord('R'), ProjectService.RUN_ID"))
1783## ])
1784## frame.SetAcceleratorTable(accelTable)
1785 isProjectDocument = document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument
1786 if wx.GetApp().IsMDI() or isProjectDocument:
1787 if not menuBar.FindItemById(ProjectService.ADD_FILES_TO_PROJECT_ID):
b792147d 1788 projectMenu.Append(ProjectService.ADD_FILES_TO_PROJECT_ID, _("Add &Files to Project..."), _("Adds a document to the current project"))
1f780e48
RD
1789 wx.EVT_MENU(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessEvent)
1790 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
b792147d 1791 if not menuBar.FindItemById(ProjectService.ADD_ALL_FILES_TO_PROJECT_ID):
6f1a3f9c 1792 projectMenu.Append(ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, _("Add Directory Files to Project..."), _("Adds a directory's documents to the current project"))
b792147d
RD
1793 wx.EVT_MENU(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessEvent)
1794 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1f780e48
RD
1795 if not menuBar.FindItemById(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID):
1796 projectMenu.Append(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, _("&Add Active File to Project..."), _("Adds the active document to a project"))
1797 wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent)
1798 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
1799 viewMenuIndex = menuBar.FindMenu(_("&View"))
1800 menuBar.Insert(viewMenuIndex + 1, projectMenu, _("&Project"))
1801 editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
1802 if not menuBar.FindItemById(ProjectService.RENAME_ID):
1f780e48
RD
1803 editMenu.Append(ProjectService.RENAME_ID, _("&Rename"), _("Renames the active item"))
1804 wx.EVT_MENU(frame, ProjectService.RENAME_ID, frame.ProcessEvent)
1805 wx.EVT_UPDATE_UI(frame, ProjectService.RENAME_ID, frame.ProcessUpdateUIEvent)
b792147d
RD
1806 if not menuBar.FindItemById(ProjectService.DELETE_FILE_ID):
1807 editMenu.Append(ProjectService.DELETE_FILE_ID, _("Delete File"), _("Delete the file from the project and file system."))
1808 wx.EVT_MENU(frame, ProjectService.DELETE_FILE_ID, frame.ProcessEvent)
1809 wx.EVT_UPDATE_UI(frame, ProjectService.DELETE_FILE_ID, frame.ProcessUpdateUIEvent)
1f780e48
RD
1810
1811 return True
1812
1813
1814 def OnCloseFrame(self, event):
1815 if not self.GetView():
1816 return True
1817
1818 if wx.GetApp().IsMDI():
1819 # close all non-project documents first
1820 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
1821 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
1822 if not self.GetDocumentManager().CloseDocument(document, False):
1823 return False
1824
1825 # write project config afterwards because user may change filenames on closing of new documents
1826 self.GetView().WriteProjectConfig() # Called onCloseWindow in all of the other services but needed to be factored out for ProjectService since it is called elsewhere
1827
1828 # close all project documents after closing other documents
1829 # because user may save a new document with a new name or cancel closing a document
1830 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
1831 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1832 if not document.OnSaveModified():
1833 return False
1834
1835 # This is called when any SDI frame is closed, so need to check if message window is closing or some other window
1836 elif self.GetView() == event.GetEventObject().GetView():
1837 self.SetView(None)
1838 return True
1839
1840
1841 #----------------------------------------------------------------------------
1842 # Event Processing Methods
1843 #----------------------------------------------------------------------------
1844
1845 def ProcessEventBeforeWindows(self, event):
1846 id = event.GetId()
26ee3a06 1847
1f780e48
RD
1848 if id == wx.ID_CLOSE_ALL:
1849 self.OnFileCloseAll(event)
1850 return True
26ee3a06
RD
1851
1852 elif id == wx.ID_CLOSE:
1853 document = self.GetDocumentManager().GetCurrentDocument()
1854 if document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1855 self.OnProjectClose(event)
1856 return True
1857 else:
1858 return False
1859 return False
1860
1861
1862 def ProcessUpdateUIEventBeforeWindows(self, event):
1863 id = event.GetId()
1864
1865 if id == wx.ID_CLOSE_ALL:
1866 for document in self.GetDocumentManager().GetDocuments():
1867 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
1868 event.Enable(True)
1869 return True
1870
1871 event.Enable(False)
1872 return True
1873
1874 elif id == wx.ID_CLOSE:
1875 document = self.GetDocumentManager().GetCurrentDocument()
1876 if document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
1877 projectFilenames = self.GetView().GetSelectedProjects()
1878 if projectFilenames and len(projectFilenames):
1879 event.Enable(True)
1880 else:
1881 event.Enable(False)
1882 return True
1883
1f780e48
RD
1884 return False
1885
1886
1887 def ProcessEvent(self, event):
1888 if Service.Service.ProcessEvent(self, event):
1889 return True
1890
1891 id = event.GetId()
1892 if id == ProjectService.RUN_SELECTED_PM_ID:
1893 self.OnRunProcessModel(event, runSelected=True)
1894 return True
1895 elif id == ProjectService.RUN_CURRENT_PM_ID:
1896 self.OnRunProcessModel(event, runCurrentFile=True)
1897 return True
1898 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1899 self.OnAddCurrentFileToProject(event)
1900 return True
1901 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1902 if self.GetView():
1903 return self.GetView().ProcessEvent(event)
1904 else:
1905 return False
1906 else:
1907 return False
1908
1909
1910 def ProcessUpdateUIEvent(self, event):
1911 if Service.Service.ProcessUpdateUIEvent(self, event):
1912 return True
1913
1914 id = event.GetId()
b792147d
RD
1915 if (id == ProjectService.RUNPM_ID
1916 or id == ProjectService.RUN_SELECTED_PM_ID
1917 or id == ProjectService.RUN_CURRENT_PM_ID):
1f780e48
RD
1918 event.Enable(self._HasOpenedProjects() and self._HasProcessModel())
1919 return True
1f780e48
RD
1920 elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
1921 event.Enable(self._CanAddCurrentFileToProject())
1922 return True
b792147d
RD
1923 elif (id == ProjectService.ADD_FILES_TO_PROJECT_ID
1924 or id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID
1925 or id == ProjectService.RENAME_ID
1926 or id == ProjectService.OPEN_SELECTION_ID
1927 or id == ProjectService.DELETE_FILE_ID):
1f780e48
RD
1928 event.Enable(False)
1929 return True
1930 elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID:
1931 if self.GetView():
1932 return self.GetView().ProcessUpdateUIEvent(event)
1933 else:
1934 return False
1935 else:
1936 return False
1937
1938
1939 def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False):
1940 project = self.GetView().GetDocument()
1941
1942 if project:
1943 ext = None
1944 for template in self.GetDocumentManager().GetTemplates():
1945 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1946 ext = template.GetDefaultExtension()
1947 break;
1948 if not ext:
1949 return
1950
1951 files = filter(lambda f: f.endswith(ext), project.GetFiles())
1952 if not files:
1953 return
1954
1955 docs = wx.GetApp().GetDocumentManager().GetDocuments()
1956 for doc in docs:
1957 if doc.GetFilename() in files and doc.GetDocumentTemplate().GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
1958 if not doc.GetProcessModel().beginProcess:
1959 wx.MessageBox(_("Cannot run process. No begin action found."), _("Run Process"))
1960 return
1961
1962 filesModified = False
1963 for doc in docs:
1964 if doc.IsModified():
1965 filesModified = True
1966 break
1967 if filesModified:
1968 frame = self.GetView().GetFrame()
1969 yesNoMsg = wx.MessageDialog(frame,
1970 _("Files have been modified. Process may not reflect your current changes.\n\nWould you like to save all files before running?"),
1971 _("Run Process"),
b792147d 1972 wx.YES_NO|wx.ICON_QUESTION
1f780e48
RD
1973 )
1974 if yesNoMsg.ShowModal() == wx.ID_YES:
1975 wx.GetTopLevelParent(frame).OnFileSaveAll(None)
1976
1977 if runCurrentFile:
1978 fileToRun = self.GetDocumentManager().GetCurrentDocument().GetFilename()
1979 elif runSelected:
1980 fileToRun = self.GetView().GetSelectedFile()
1981 elif len(files) > 1:
1982 files.sort(lambda a, b: cmp(os.path.basename(a).lower(), os.path.basename(b).lower()))
1983 strings = map(lambda file: os.path.basename(file), files)
1984 res = wx.GetSingleChoiceIndex(_("Select a process to run:"),
1985 _("Run"),
1986 strings,
1987 project.GetFirstView()._GetParentFrame())
1988 if res == -1:
1989 return
1990 fileToRun = files[res]
1991 else:
1992 fileToRun = files[0]
1993
1994 self.RunProcessModel(fileToRun)
1995
1996
1997 def RunProcessModel(self, fileToRun):
1998 for runHandler in self.GetRunHandlers():
1999 if runHandler.RunProjectFile(fileToRun):
2000 return
2001 os.system('"' + fileToRun + '"')
2002
2003
2004 def _HasProcessModel(self):
2005 project = self.GetView().GetDocument()
2006
2007 if project:
2008 ext = None
2009 for template in self.GetDocumentManager().GetTemplates():
2010 if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
2011 ext = template.GetDefaultExtension()
2012 break;
2013 if not ext:
2014 return False
2015
2016 files = filter(lambda f: f.endswith(ext), project.GetFiles())
2017 if not files:
2018 return False
2019
2020 if len(files):
2021 return True
2022
2023 return False
2024
2025
2026 def _HasOpenedProjects(self):
2027 for document in self.GetDocumentManager().GetDocuments():
2028 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2029 return True
2030 return False
2031
2032
2033 def _HasCurrentFile(self):
2034 currentDoc = self.GetDocumentManager().GetCurrentDocument()
2035 return currentDoc
2036
2037
2038 def _CanAddCurrentFileToProject(self):
2039 currentDoc = self.GetDocumentManager().GetCurrentDocument()
2040 if not currentDoc:
2041 return False
2042 if currentDoc.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2043 return False
2044 if not currentDoc._savedYet:
2045 return False
2046 for document in self.GetDocumentManager().GetDocuments():
2047 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2048 return True
2049 return False # There are no documents open
2050
2051
2052 def GetFilesFromCurrentProject(self):
2053 view = self.GetView()
2054 if view:
2055 project = view.GetDocument()
2056 if project:
2057 return project.GetFiles()
2058 return None
2059
2060
2061 def GetCurrentProject(self):
2062 view = self.GetView()
2063 if view:
2064 return view.GetDocument()
2065 return None
2066
2067
2068 def FindProjectByFile(self, filename):
2069 for document in self.GetDocumentManager().GetDocuments():
2070 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2071 if document.GetFilename() == filename:
2072 return document
2073 elif document.IsFileInProject(filename):
2074 return document
2075 return None
2076
2077
2078 def GetCurrentProjectNames(self):
2079 projects = []
2080 for document in self.GetDocumentManager().GetDocuments():
2081 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2082 projects.append(document)
2083 if not projects:
2084 return
2085 projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower()))
2086 strings = map(lambda project: project.GetPrintableName(), projects)
2087 return strings
2088
b792147d 2089
1f780e48
RD
2090 def OnAddCurrentFileToProject(self, event):
2091 if not self._CanAddCurrentFileToProject():
2092 return
2093 projects = []
2094 for document in self.GetDocumentManager().GetDocuments():
2095 if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument:
2096 projects.append(document)
2097 if not projects:
2098 return
2099 projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower()))
2100 strings = map(lambda project: project.GetPrintableName(), projects)
2101 res = wx.GetSingleChoiceIndex(_("Select a project to add the file to:"),
2102 _("Add to Project"),
2103 strings,
2104 self.GetDocumentManager().FindSuitableParent())
2105 if res == -1:
2106 return
2107 file = self.GetDocumentManager().GetCurrentDocument().GetFilename()
2108 projects[res].GetCommandProcessor().Submit(ProjectAddFilesCommand(projects[res], [file]))
2109 self.GetView().Activate(True) # after add, should put focus on project editor
2110
2111
26ee3a06
RD
2112 def OnProjectClose(self, event):
2113 projectFilenames = self.GetView().GetSelectedProjects()
2114 for filename in projectFilenames:
2115 doc = self.FindProjectByFile(filename)
2116 if doc:
2117 self.GetDocumentManager().CloseDocument(doc, False)
2118
2119
1f780e48
RD
2120 def OnFileCloseAll(self, event):
2121 for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted
2122 if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument:
2123 if not self.GetDocumentManager().CloseDocument(document, False):
2124 return
2125 # document.DeleteAllViews() # Implicitly delete the document when the last view is removed
2126
2127
2128 def OpenSavedProjects(self):
2129 config = wx.ConfigBase_Get()
2130 openedDocs = False
2131 if config.ReadInt("ProjectSaveDocs", True):
2132 docString = config.Read("ProjectSavedDocs")
2133 if docString:
2134 doc = None
2135 for fileName in eval(docString):
2136 if isinstance(fileName, types.StringTypes):
2137 if os.path.exists(fileName):
2138 doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT)
2139
2140 if doc:
2141 openedDocs = True
2142 expandedString = config.Read("ProjectExpandedSavedDocs")
2143 if expandedString:
2144 view = doc.GetFirstView()
2145 view.SetExpandedProjects(eval(expandedString))
2146 return openedDocs
2147
b792147d
RD
2148
2149class ProjectEditorMoveCommand(wx.lib.docview.Command):
2150
2151 def __init__(self, view, newPositionItem, item):
2152 wx.lib.docview.Command.__init__(self, canUndo = True)
2153 self._view = view
2154 self._item = item
2155 self._file = view._treeCtrl.GetLongFilename(item)
2156 if view._IsItemFile(item):
2157 self._projectOld = view._GetItemProject(item)
2158 else: # view._IsItemProject(item):
2159 self._projectOld = None
2160 self._projectNew = view._GetItemProject(newPositionItem)
2161
2162
2163 def GetName(self):
2164 return _("Move File %s") % os.path.basename(self._file)
2165
2166
2167 def Do(self):
2168 if self._projectOld:
2169 self._projectOld.RemoveFile(self._file)
2170 if self._projectNew:
2171 self._projectNew.AddFile(self._file)
2172 return True
2173
2174
2175 def Undo(self):
2176 if self._projectNew:
2177 self._projectNew.RemoveFile(self._file)
2178 if self._projectOld:
2179 self._projectOld.AddFile(self._file)
2180 return True
2181
2182
1f780e48
RD
2183#----------------------------------------------------------------------------
2184# Icon Bitmaps - generated by encode_bitmaps.py
2185#----------------------------------------------------------------------------
2186from wx import ImageFromStream, BitmapFromImage
1f780e48
RD
2187import cStringIO
2188
2189
2190def getProjectData():
2191 return \
2192'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
2193\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
2194\x00\x00[IDAT8\x8d\xc5\x93\xc1\n\xc00\x08C\x8d\xf6\xff\xffX\xb3Sa-\xf6`;:O\n\
2195\x12\x1fj\x0059\t\xed\t\xc3\xc9pn\x0b\x88\x88@\rU\x81\xf6.\x18N\xa8aE\x92\rh\
2196YC\x85\xa4D\x90\x91\xdc%\xf8w\x07+\xd1\xfbW\x98\xc5\x8f\t\x86W\xee\x93+\xbe\
2197\xc0gn\xdc\x8d\x07\xab"<iG\x8e\xa9\r\x00\x00\x00\x00IEND\xaeB`\x82'
2198
2199def getProjectBitmap():
2200 return BitmapFromImage(getProjectImage())
2201
2202def getProjectImage():
2203 stream = cStringIO.StringIO(getProjectData())
2204 return ImageFromStream(stream)
2205
2206def getProjectIcon():
bbf7159c 2207 return wx.IconFromBitmap(getProjectBitmap())
1f780e48
RD
2208
2209
2210#----------------------------------------------------------------------------
2211
2212def getBlankData():
2213 return \
bbf7159c
RD
2214'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
2215\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
2216\x00\x00]IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\x87\xb6C\x11\x89\
2217\xa8X:\xd4\x13\x03:\x1b\x01\xa45T\xd4\xefBsh\xd7Hk\xdc\x02\x00@\x8a\x19$\xa1\
22189\x14A,\x95\xf3\x82G)\xd3\x00\xf24\xf7\x90\x1ev\x07\xee\x1e\xf4:\xc1J?\xe0\
2219\x0b\x80\xc7\x1d\xf8\x1dg\xc4\xea7\x96G8\x00\xa8\x91\x19(\x85#P\x7f\x00\x00\
2220\x00\x00IEND\xaeB`\x82'
1f780e48
RD
2221
2222
2223def getBlankBitmap():
2224 return BitmapFromImage(getBlankImage())
2225
2226def getBlankImage():
2227 stream = cStringIO.StringIO(getBlankData())
2228 return ImageFromStream(stream)
2229
2230def getBlankIcon():
bbf7159c 2231 return wx.IconFromBitmap(getBlankBitmap())
1f780e48 2232