]>
Commit | Line | Data |
---|---|---|
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 | ||
13 | import wx.lib.docview | |
14 | import wx.lib.pydocview | |
15 | import types | |
16 | import os | |
17 | import os.path | |
18 | import wx | |
19 | from wxPython.lib.rcsizer import RowColSizer | |
20 | import time | |
21 | import Service | |
1f780e48 | 22 | import sys |
2eeaec19 | 23 | import activegrid.util.xmlutils |
1f780e48 | 24 | import UICommon |
b792147d | 25 | import Wizard |
6f1a3f9c | 26 | import SVNService |
1f780e48 RD |
27 | from IDE import ACTIVEGRID_BASE_IDE |
28 | if not ACTIVEGRID_BASE_IDE: | |
29 | import ProcessModelEditor | |
6f1a3f9c | 30 | from SVNService import SVN_INSTALLED |
1f780e48 RD |
31 | |
32 | _ = wx.GetTranslation | |
33 | ||
34 | if wx.Platform == '__WXMSW__': | |
35 | _WINDOWS = True | |
36 | else: | |
37 | _WINDOWS = False | |
38 | ||
39 | ||
b792147d RD |
40 | #---------------------------------------------------------------------------- |
41 | # Constants | |
42 | #---------------------------------------------------------------------------- | |
43 | SPACE = 10 | |
44 | HALF_SPACE = 5 | |
45 | ||
46 | ||
1f780e48 RD |
47 | #---------------------------------------------------------------------------- |
48 | # XML Marshalling Methods | |
49 | #---------------------------------------------------------------------------- | |
b792147d | 50 | |
1f780e48 | 51 | def load(fileObject): |
2eeaec19 | 52 | return activegrid.util.xmlutils.defaultLoad(fileObject, knownTypes={"projectmodel" : ProjectModel}) |
1f780e48 RD |
53 | |
54 | def save(fileObject, projectModel): | |
2eeaec19 | 55 | activegrid.util.xmlutils.defaultSave(fileObject, projectModel, prettyPrint=True, knownTypes={"projectmodel" : ProjectModel}) |
1f780e48 RD |
56 | |
57 | ||
58 | #---------------------------------------------------------------------------- | |
59 | # Classes | |
60 | #---------------------------------------------------------------------------- | |
61 | ||
62 | class 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 |
74 | class 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 |
259 | class 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 |
332 | class 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 |
345 | class 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 | ||
369 | class 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 | |
418 | class 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 | ||
441 | class 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 |
528 | class 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() | |
2eeaec19 RD |
1058 | choices.insert(0, _("All (%s)") % allfilter) # first item |
1059 | choices.append(_("Any (*.*)")) # last item | |
bbb28897 | 1060 | filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices) |
b792147d RD |
1061 | filterChoice.SetSelection(0) |
1062 | filterChoice.SetToolTipString(_("Select file type filter.")) | |
bbb28897 RD |
1063 | flexGridSizer.Add(wx.StaticText(frame, -1, _("Files of type:")), 0, wx.ALIGN_CENTER_VERTICAL) |
1064 | flexGridSizer.Add(filterChoice, 1, wx.EXPAND) | |
1065 | ||
1066 | contentSizer.Add(flexGridSizer, 0, wx.ALL|wx.EXPAND, SPACE) | |
b792147d RD |
1067 | |
1068 | subfolderCtrl = wx.CheckBox(frame, -1, _("Add files from subdirectories")) | |
1069 | subfolderCtrl.SetValue(True) | |
bbb28897 | 1070 | contentSizer.Add(subfolderCtrl, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, SPACE) |
b792147d | 1071 | |
bbb28897 | 1072 | buttonSizer = wx.BoxSizer(wx.HORIZONTAL) |
b792147d RD |
1073 | findBtn = wx.Button(frame, wx.ID_OK, _("Add")) |
1074 | findBtn.SetDefault() | |
bbb28897 | 1075 | buttonSizer.Add(findBtn, 0, wx.RIGHT, HALF_SPACE) |
b792147d | 1076 | buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0) |
bbb28897 | 1077 | contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE) |
b792147d | 1078 | |
bbb28897 | 1079 | frame.SetSizer(contentSizer) |
b792147d RD |
1080 | frame.Fit() |
1081 | ||
1082 | status = frame.ShowModal() | |
1083 | ||
1084 | passedCheck = False | |
1085 | while status == wx.ID_OK and not passedCheck: | |
1086 | if not os.path.exists(dirCtrl.GetValue()): | |
1087 | dlg = wx.MessageDialog(frame, | |
1088 | _("'%s' does not exist.") % dirCtrl.GetValue(), | |
1089 | _("Find in Directory"), | |
1090 | wx.OK | wx.ICON_EXCLAMATION | |
1091 | ) | |
1092 | dlg.ShowModal() | |
1093 | dlg.Destroy() | |
1094 | ||
1095 | status = frame.ShowModal() | |
1096 | else: | |
1097 | passedCheck = True | |
1098 | ||
1099 | if status == wx.ID_OK: | |
1100 | frame.Destroy() | |
1101 | ||
1102 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) | |
1103 | ||
1104 | doc = self.GetDocument() | |
1105 | searchSubfolders = subfolderCtrl.IsChecked() | |
1106 | dirString = dirCtrl.GetValue() | |
1107 | ||
1108 | if os.path.isfile(dirString): | |
1109 | # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter. | |
1110 | # We'll assume they know what they're doing. | |
1111 | paths = [dirString] | |
1112 | else: | |
1113 | paths = [] | |
1114 | ||
1115 | index = filterChoice.GetSelection() | |
2eeaec19 RD |
1116 | lastIndex = filterChoice.GetCount()-1 |
1117 | if index and index != lastIndex: # if not All or Any | |
b792147d RD |
1118 | template = visibleTemplates[index-1] |
1119 | ||
1120 | # do search in files on disk | |
1121 | for root, dirs, files in os.walk(dirString): | |
1122 | if not searchSubfolders and root != dirString: | |
1123 | break | |
1124 | ||
1125 | for name in files: | |
2eeaec19 | 1126 | if index == 0: # All |
b792147d RD |
1127 | for template in visibleTemplates: |
1128 | if template.FileMatchesTemplate(name): | |
1129 | filename = os.path.join(root, name) | |
1130 | ||
1131 | # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it. | |
1132 | if doc.IsFileInProject(filename): | |
1133 | break | |
1134 | ||
1135 | paths.append(filename) | |
1136 | break | |
2eeaec19 RD |
1137 | elif index == lastIndex: # Any |
1138 | filename = os.path.join(root, name) | |
1139 | # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it. | |
1140 | if not doc.IsFileInProject(filename): | |
1141 | paths.append(filename) | |
b792147d RD |
1142 | else: # use selected filter |
1143 | if template.FileMatchesTemplate(name): | |
1144 | filename = os.path.join(root, name) | |
1145 | # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it. | |
1146 | if not doc.IsFileInProject(filename): | |
1147 | paths.append(filename) | |
1148 | ||
1149 | wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) | |
1150 | ||
1151 | doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths)) | |
1152 | self.Activate(True) # after add, should put focus on project editor | |
1153 | else: | |
1154 | frame.Destroy() | |
1155 | ||
1156 | ||
1f780e48 RD |
1157 | def DoAddFilesToProject(self, filenames): |
1158 | # method used by Drag-n-Drop to add files to current Project | |
1159 | self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), filenames)) | |
1160 | ||
1161 | ||
1162 | def DoSelectFiles(self, filenames): | |
1163 | # method used by Drag-n-Drop to select files in current Project | |
b792147d | 1164 | self._treeCtrl.UnselectAll() |
1f780e48 RD |
1165 | for file in filenames: |
1166 | item = self._GetFileItem(longFileName=file) | |
1167 | if item: | |
1168 | self._treeCtrl.SelectItem(item, True) | |
1169 | self._treeCtrl.EnsureVisible(item) | |
1170 | ||
1171 | ||
1172 | def DoSelectProject(self, x, y): | |
1173 | # method used by Drag-n-Drop to set current Project based on cursor position | |
1174 | item, flag = self._treeCtrl.HitTest((x,y)) | |
1175 | if not item: | |
1176 | return False | |
1177 | ||
1178 | project = self._GetItemProject(item) | |
1179 | if not project: | |
1180 | return False | |
1181 | ||
1182 | projectItem = self._GetProjectItem(project) | |
1183 | self._treeCtrl.UnselectAll() | |
1184 | self._treeCtrl.SelectItem(projectItem) | |
1185 | return True | |
1186 | ||
1187 | ||
1188 | def OnFocus(self, event): | |
1189 | wx.GetApp().GetDocumentManager().ActivateView(self) | |
1190 | event.Skip() | |
1191 | ||
1192 | ||
1193 | def OnKillFocus(self, event): | |
1194 | # Get the top MDI window and "activate" it since it is already active from the perspective of the MDIParentFrame | |
1195 | # wxBug: Would be preferable to call OnActivate, but have casting problem, so added Activate method to docview.DocMDIChildFrame | |
1196 | 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 | |
1197 | childFrame = wx.GetApp().GetTopWindow().GetActiveChild() | |
1198 | if childFrame: | |
1199 | childFrame.Activate() | |
1200 | event.Skip() | |
1201 | ||
1202 | ||
1203 | def OnRightClick(self, event): | |
1204 | self.Activate(True) | |
1205 | if not self._treeCtrl.GetSelections(): | |
1206 | return | |
1207 | if len(self._treeCtrl.GetSelections()) == 1 and self._IsItemRoot(self._treeCtrl.GetSelections()[0]): | |
1208 | return # Don't do a menu if it's just the root item selected | |
1209 | menu = wx.Menu() | |
1210 | if self._HasFilesSelected(): # Files context | |
1211 | menu.Append(ProjectService.OPEN_SELECTION_ID, _("&Open"), _("Opens the selection")) | |
1212 | menu.Enable(ProjectService.OPEN_SELECTION_ID, True) | |
1213 | wx.EVT_MENU(self._GetParentFrame(), ProjectService.OPEN_SELECTION_ID, self.OnOpenSelection) | |
1214 | itemIDs = [None] | |
1215 | for item in self._treeCtrl.GetSelections(): | |
1216 | if self._IsItemProcessModelFile(item): | |
1217 | itemIDs = [None, ProjectService.RUN_SELECTED_PM_ID, None] | |
1218 | break | |
1219 | else: # Project context | |
1220 | itemIDs = [wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS, None] | |
1221 | menuBar = self._GetParentFrame().GetMenuBar() | |
6f1a3f9c | 1222 | itemIDs = itemIDs + [ProjectService.ADD_FILES_TO_PROJECT_ID, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, ProjectService.REMOVE_FROM_PROJECT] |
26ee3a06 | 1223 | svnIDs = [SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID] |
6f1a3f9c RD |
1224 | if SVN_INSTALLED: |
1225 | itemIDs = itemIDs + [None, SVNService.SVNService.SVN_UPDATE_ID, SVNService.SVNService.SVN_CHECKIN_ID, SVNService.SVNService.SVN_REVERT_ID] | |
26ee3a06 | 1226 | globalIDs = [wx.ID_UNDO, wx.ID_REDO, wx.ID_CLOSE, wx.ID_SAVE, wx.ID_SAVEAS] |
6f1a3f9c | 1227 | 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 |
1228 | for itemID in itemIDs: |
1229 | if not itemID: | |
1230 | menu.AppendSeparator() | |
1231 | else: | |
1232 | if itemID == ProjectService.RUN_SELECTED_PM_ID: | |
1233 | menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process")) | |
1234 | wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.OnRunSelectedPM) | |
1235 | elif itemID == ProjectService.REMOVE_FROM_PROJECT: | |
1236 | menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project")) | |
1237 | wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear) | |
1238 | wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent) | |
1239 | else: | |
26ee3a06 | 1240 | svnService = wx.GetApp().GetService(SVNService.SVNService) |
1f780e48 RD |
1241 | item = menuBar.FindItemById(itemID) |
1242 | if item: | |
26ee3a06 RD |
1243 | if itemID in svnIDs: |
1244 | if SVN_INSTALLED and svnService: | |
1245 | wx.EVT_MENU(self._GetParentFrame(), itemID, svnService.ProcessEvent) | |
1246 | elif itemID in globalIDs: | |
1247 | pass | |
1248 | else: | |
1249 | wx.EVT_MENU(self._treeCtrl, itemID, self.ProcessEvent) | |
1f780e48 RD |
1250 | menu.Append(itemID, item.GetLabel()) |
1251 | self._treeCtrl.PopupMenu(menu, wx.Point(event.GetX(), event.GetY())) | |
1252 | menu.Destroy() | |
1253 | ||
b792147d | 1254 | |
1f780e48 RD |
1255 | def OnRunSelectedPM(self, event): |
1256 | projectService = wx.GetApp().GetService(ProjectService) | |
b792147d RD |
1257 | if projectService: |
1258 | projectService.OnRunProcessModel(event, runSelected=True) | |
1259 | ||
1f780e48 RD |
1260 | |
1261 | def OnRename(self, event): | |
1262 | if self._treeCtrl.GetSelections(): | |
1263 | self._treeCtrl.EditLabel(self._treeCtrl.GetSelections()[0]) | |
1264 | ||
1265 | ||
1266 | def OnBeginLabelEdit(self, event): | |
1267 | self._editingSoDontKillFocus = True | |
1268 | item = event.GetItem() | |
1269 | if not self._IsItemFile(item) and not self._IsItemProject(item): | |
1270 | event.Veto() | |
1271 | ||
1272 | ||
1273 | def OnEndLabelEdit(self, event): | |
1274 | self._editingSoDontKillFocus = False | |
1275 | item = event.GetItem() | |
1276 | newName = event.GetLabel() | |
1277 | if not newName or (not self._IsItemFile(item) and not self._IsItemProject(item)): | |
1278 | event.Veto() | |
1279 | return | |
1280 | if self._IsItemFile(item): | |
1281 | oldFile = self._GetItemFile(item) | |
1282 | newFile = os.path.join(os.path.split(oldFile)[0], newName) | |
b792147d RD |
1283 | project = self._GetItemProject(item) |
1284 | if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile)): | |
1f780e48 RD |
1285 | event.Veto() |
1286 | return | |
1287 | self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(self._treeCtrl.GetSelections()[0])) | |
1288 | elif self._IsItemProject(item): | |
1289 | oldFile = self._GetItemProject(item).GetFilename() | |
1290 | newFile = os.path.join(os.path.split(oldFile)[0], newName) | |
b792147d RD |
1291 | project = self._GetItemProject(item) |
1292 | if not project.GetCommandProcessor().Submit(ProjectRenameFileCommand(project, oldFile, newFile, True)): | |
1f780e48 RD |
1293 | event.Veto() |
1294 | return | |
1295 | self._treeCtrl.SortChildren(self._treeCtrl.GetRootItem()) | |
1296 | ||
1297 | ||
1298 | def CanPaste(self): | |
1299 | # wxBug: Should be able to use IsSupported/IsSupportedFormat here | |
1300 | #fileDataObject = wx.FileDataObject() | |
1301 | #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject) | |
1302 | if not wx.TheClipboard.IsOpened(): | |
1303 | if wx.TheClipboard.Open(): | |
1304 | fileDataObject = wx.FileDataObject() | |
1305 | hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject) | |
1306 | wx.TheClipboard.Close() | |
1307 | else: | |
1308 | hasFilesInClipboard = False | |
1309 | return hasFilesInClipboard | |
1310 | ||
1311 | ||
1312 | def OnCut(self, event): | |
b792147d RD |
1313 | self.OnCopy(event) |
1314 | self.OnClear(event) | |
1f780e48 RD |
1315 | |
1316 | ||
1317 | def OnCopy(self, event): | |
1318 | fileDataObject = wx.FileDataObject() | |
1319 | items = self._treeCtrl.GetSelections() | |
1320 | for item in items: | |
1321 | if self._IsItemFile(item): | |
b792147d | 1322 | file = self._treeCtrl.GetLongFilename(item) |
1f780e48 RD |
1323 | fileDataObject.AddFile(file) |
1324 | if len(fileDataObject.GetFilenames()) > 0 and wx.TheClipboard.Open(): | |
1325 | wx.TheClipboard.SetData(fileDataObject) | |
1326 | wx.TheClipboard.Close() | |
1327 | ||
1328 | ||
1329 | def OnPaste(self, event): | |
1330 | if wx.TheClipboard.Open(): | |
1331 | fileDataObject = wx.FileDataObject() | |
1332 | if wx.TheClipboard.GetData(fileDataObject): | |
1333 | self.GetDocument().GetCommandProcessor().Submit(ProjectAddFilesCommand(self.GetDocument(), fileDataObject.GetFilenames())) | |
1334 | wx.TheClipboard.Close() | |
1335 | ||
1336 | ||
1337 | def OnClear(self, event): | |
b792147d RD |
1338 | items = self._treeCtrl.GetSelections() |
1339 | files = [] | |
1340 | for item in items: | |
1341 | if self._IsItemFile(item): | |
1342 | files.append((self._GetItemProject(item), self._GetItemFile(item))) | |
1343 | self.GetDocument().GetCommandProcessor().Submit(ProjectRemoveFilesCommand(files)) | |
1f780e48 RD |
1344 | |
1345 | ||
b792147d RD |
1346 | def OnDeleteFile(self, event): |
1347 | yesNoMsg = wx.MessageDialog(self.GetFrame(), | |
1348 | _("Delete cannot be reversed.\n\nRemove the selected files from the\nprojects and file system permanently?"), | |
1349 | _("Delete File"), | |
1350 | wx.YES_NO|wx.ICON_QUESTION) | |
1351 | if yesNoMsg.ShowModal() == wx.ID_NO: | |
1352 | return | |
1353 | ||
1354 | items = self._treeCtrl.GetSelections() | |
1355 | files = [] | |
1356 | delFiles = [] | |
1357 | for item in items: | |
1358 | if self._IsItemFile(item): | |
1359 | filename = self._GetItemFile(item) | |
1360 | files.append((self._GetItemProject(item), filename)) | |
1361 | if filename not in delFiles: | |
1362 | delFiles.append(filename) | |
1363 | ||
1364 | # remove selected files from projects | |
1365 | projects = [] | |
1366 | for data in files: | |
1367 | proj, filename = data | |
1368 | if proj not in projects: | |
1369 | projects.append(proj) | |
1370 | for project in projects: | |
1371 | filenames = [] | |
1372 | for data in files: | |
1373 | proj, filename = data | |
1374 | if project == proj: | |
1375 | filenames.append(filename) | |
1376 | project.RemoveFiles(filenames) | |
1377 | ||
1378 | # remove selected files from file system | |
1379 | for filename in delFiles: | |
1380 | if os.path.exists(filename): | |
1381 | try: | |
1382 | os.remove(filename) | |
1383 | except: | |
1384 | wx.MessageBox("Could not delete '%s'. %s" % (os.path.basename(filename), sys.exc_value), | |
1385 | _("Delete File"), | |
1386 | wx.OK | wx.ICON_EXCLAMATION) | |
1387 | ||
1388 | ||
1f780e48 RD |
1389 | def OnKeyPressed(self, event): |
1390 | key = event.KeyCode() | |
1391 | if key == wx.WXK_DELETE: | |
1392 | self.OnClear(event) | |
1393 | else: | |
1394 | event.Skip() | |
1395 | ||
1396 | ||
1397 | def OnSelectAll(self, event): | |
1398 | project = self.GetDocument() | |
1399 | if project: | |
1400 | self._treeCtrl.UnselectAll() | |
1401 | for child in self._GetChildItems(self._GetProjectItem(project)): | |
1402 | self._treeCtrl.SelectItem(child) | |
1403 | ||
1404 | ||
1405 | def OnOpenSelectionSDI(self, event): | |
1406 | # Do a call after so that the second mouseclick on a doubleclick doesn't reselect the project window | |
1407 | wx.CallAfter(self.OnOpenSelection, None) | |
1408 | ||
1409 | ||
1410 | def OnOpenSelection(self, event): | |
1411 | doc = None | |
1412 | try: | |
1413 | items = self._treeCtrl.GetSelections() | |
1414 | for item in items: | |
1415 | if self._IsItemFile(item): | |
1416 | filepath = self._GetItemFile(item) | |
1417 | if not os.path.exists(filepath): | |
1418 | msgTitle = wx.GetApp().GetAppName() | |
1419 | if not msgTitle: | |
1420 | msgTitle = _("File Not Found") | |
1421 | yesNoMsg = wx.MessageDialog(self.GetFrame(), | |
1422 | _("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)), | |
1423 | msgTitle, | |
b792147d | 1424 | wx.YES_NO|wx.ICON_QUESTION |
1f780e48 RD |
1425 | ) |
1426 | if yesNoMsg.ShowModal() == wx.ID_NO: | |
1427 | continue | |
1428 | findFile = wx.FileDialog(self.GetFrame(), | |
1429 | _("Choose a file"), | |
1430 | wx.lib.docview.PathOnly(filepath), | |
1431 | wx.lib.docview.FileNameFromPath(filepath), | |
1432 | style = wx.OPEN | |
1433 | ) | |
1434 | if findFile.ShowModal() == wx.ID_OK and findFile.GetPath(): | |
1435 | newpath = findFile.GetPath() | |
1436 | else: | |
1437 | newpath = None | |
1438 | findFile.Destroy() | |
1439 | if newpath: | |
1440 | # update Project Model with new location | |
6f1a3f9c RD |
1441 | project = self._GetItemProject(item) |
1442 | project.RemoveFile(filepath) | |
1443 | project.AddFile(newpath) | |
1f780e48 RD |
1444 | filepath = newpath |
1445 | ||
1446 | doc = self.GetDocumentManager().CreateDocument(filepath, wx.lib.docview.DOC_SILENT) | |
b792147d RD |
1447 | if not doc: |
1448 | shortFilename = self._treeCtrl.GetItemText(item) | |
1449 | if shortFilename.endswith(".agp"): | |
1450 | projItem = self._GetProjectItem(shortFilename=shortFilename) | |
1451 | self._treeCtrl.UnselectAll() | |
1452 | if not self._treeCtrl.IsExpanded(projItem): | |
1453 | self._treeCtrl.Expand(projItem) | |
1454 | if not self._treeCtrl.IsVisible(projItem): | |
1455 | self._treeCtrl.EnsureVisible(projItem) | |
1456 | if not self._treeCtrl.IsSelected(projItem): | |
1457 | self._treeCtrl.SelectItem(projItem) | |
1f780e48 RD |
1458 | |
1459 | except IOError, (code, message): | |
1460 | msgTitle = wx.GetApp().GetAppName() | |
1461 | if not msgTitle: | |
1462 | msgTitle = _("File Error") | |
1463 | wx.MessageBox("Could not open '%s'." % wx.lib.docview.FileNameFromPath(filepath), | |
1464 | msgTitle, | |
1465 | wx.OK | wx.ICON_EXCLAMATION, | |
1466 | self.GetFrame()) | |
1467 | ||
1468 | ||
1469 | #---------------------------------------------------------------------------- | |
1470 | # Convenience methods | |
1471 | #---------------------------------------------------------------------------- | |
1472 | ||
1473 | def _HasFiles(self): | |
1474 | if not self._treeCtrl: | |
1475 | return False | |
1476 | return self._treeCtrl.GetCount() > 1 # 1 item = root item, don't count as having files | |
1477 | ||
1478 | ||
b792147d | 1479 | def _HasSelection(self): |
1f780e48 RD |
1480 | if not self._treeCtrl: |
1481 | return False | |
b792147d | 1482 | |
1f780e48 | 1483 | items = self._treeCtrl.GetSelections() |
b792147d RD |
1484 | if items: |
1485 | return True | |
1486 | ||
1f780e48 RD |
1487 | return False |
1488 | ||
1489 | ||
1490 | def _HasFilesSelected(self): | |
1491 | if not self._treeCtrl: | |
1492 | return False | |
1493 | items = self._treeCtrl.GetSelections() | |
1494 | if not items: | |
1495 | return False | |
1496 | for item in items: | |
1497 | if not self._IsItemFile(item): | |
1498 | return False | |
1499 | return True | |
1500 | ||
1501 | ||
1502 | def _MakeProjectName(self, project): | |
1503 | return project.GetPrintableName() | |
1504 | ||
1505 | ||
1506 | # Return the tree item for a project | |
b792147d RD |
1507 | def _GetProjectItem(self, project=None, shortFilename=None): |
1508 | rootItem = self._treeCtrl.GetRootItem() | |
1509 | (child, cookie) = self._treeCtrl.GetFirstChild(rootItem) | |
1510 | while child.IsOk(): | |
1511 | if project: | |
1512 | if self._treeCtrl.GetProjectDoc(child) == project: | |
1513 | return child | |
1514 | elif shortFilename: | |
1515 | if self._treeCtrl.GetItemText(child) == shortFilename: | |
1516 | return child | |
1517 | (child, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie) | |
1f780e48 RD |
1518 | return None |
1519 | ||
1520 | ||
1521 | # Returns the project for an item, either for a project item or a file that is part of a project | |
1522 | def _GetItemProject(self, item): | |
1523 | if self._IsItemRoot(item): | |
1524 | return None | |
1525 | if self._IsItemProject(item): | |
b792147d | 1526 | return self._treeCtrl.GetProjectDoc(item) |
1f780e48 | 1527 | if self._IsItemFile(item): |
b792147d | 1528 | return self._treeCtrl.GetProjectDoc(self._treeCtrl.GetItemParent(item)) |
1f780e48 RD |
1529 | return None |
1530 | ||
1531 | ||
1532 | def _GetItemFile(self, item): | |
1533 | if self._IsItemFile(item): | |
b792147d | 1534 | return self._treeCtrl.GetLongFilename(item) |
1f780e48 RD |
1535 | else: |
1536 | return None | |
1537 | ||
1538 | ||
1539 | def _GetFileItem(self, shortFileName = None, longFileName = None): | |
1540 | """ Returns the tree item for a file given the short (display) or long (fullpath) file name. """ | |
1541 | ||
b792147d RD |
1542 | rootItem = self._treeCtrl.GetRootItem() |
1543 | (project, cookie) = self._treeCtrl.GetFirstChild(rootItem) | |
1544 | while project.IsOk(): | |
1545 | (child, cookie2) = self._treeCtrl.GetFirstChild(project) | |
1546 | while child.IsOk(): | |
1547 | if shortFileName: | |
1548 | if self._treeCtrl.GetItemText(child) == shortFileName: | |
1549 | return child | |
1550 | else: | |
1551 | if self._treeCtrl.GetLongFilename(child) == longFileName: | |
1552 | return child | |
bbb28897 | 1553 | (child, cookie2) = self._treeCtrl.GetNextChild(project, cookie2) |
b792147d RD |
1554 | (project, cookie) = self._treeCtrl.GetNextChild(rootItem, cookie) |
1555 | return None | |
1f780e48 RD |
1556 | |
1557 | ||
1558 | def _IsItemRoot(self, item): | |
1559 | return item == self._treeCtrl.GetRootItem() | |
1560 | ||
1561 | ||
1562 | def _IsItemProject(self, item): | |
b792147d | 1563 | return self._treeCtrl.GetProjectDoc(item) != None |
1f780e48 RD |
1564 | |
1565 | ||
1566 | def _IsItemFile(self, item): | |
b792147d | 1567 | return self._treeCtrl.GetProjectDoc(item) == None |
1f780e48 RD |
1568 | |
1569 | ||
1570 | def _IsItemProcessModelFile(self, item): | |
1571 | if ACTIVEGRID_BASE_IDE: | |
1572 | return False | |
1573 | ||
b792147d RD |
1574 | if self._IsItemFile(item): |
1575 | filename = self._treeCtrl.GetLongFilename(item) | |
1f780e48 RD |
1576 | ext = None |
1577 | for template in self.GetDocumentManager().GetTemplates(): | |
1578 | if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument: | |
1579 | ext = template.GetDefaultExtension() | |
1580 | break; | |
1581 | if not ext: | |
1582 | return False | |
1583 | ||
1584 | if filename.endswith(ext): | |
1585 | return True | |
1586 | ||
1587 | return False | |
1588 | ||
1589 | ||
1f780e48 RD |
1590 | def _GetChildItems(self, parentItem): |
1591 | children = [] | |
1592 | (child, cookie) = self._treeCtrl.GetFirstChild(parentItem) | |
1593 | while child.IsOk(): | |
1594 | children.append(child) | |
1595 | (child, cookie) = self._treeCtrl.GetNextChild(parentItem, cookie) | |
1596 | return children | |
1597 | ||
1598 | ||
1599 | ||
1600 | class ProjectFileDropTarget(wx.FileDropTarget): | |
1601 | ||
1602 | def __init__(self, view): | |
1603 | wx.FileDropTarget.__init__(self) | |
1604 | self._view = view | |
1605 | ||
1606 | ||
1607 | def OnDropFiles(self, x, y, filenames): | |
1608 | if self._view.DoSelectProject(x, y): | |
1609 | self._view.DoAddFilesToProject(filenames) | |
1610 | self._view.DoSelectFiles(filenames) | |
1611 | return True | |
1612 | return False | |
1613 | ||
1614 | ||
1615 | def OnDragOver(self, x, y, default): | |
1616 | if self._view.DoSelectProject(x,y): | |
1617 | return wx.DragCopy | |
1618 | return wx.DragNone | |
1619 | ||
1620 | ||
1621 | class ProjectPropertiesDialog(wx.Dialog): | |
1622 | ||
1623 | ||
1624 | def __init__(self, parent, filename): | |
1625 | wx.Dialog.__init__(self, parent, -1, _("Project Properties"), size = (310, 330)) | |
1626 | ||
1f780e48 RD |
1627 | filePropertiesService = wx.GetApp().GetService(wx.lib.pydocview.FilePropertiesService) |
1628 | ||
1629 | notebook = wx.Notebook(self, -1) | |
1630 | tab = wx.Panel(notebook, -1) | |
1631 | ||
1632 | gridSizer = RowColSizer() | |
1633 | ||
1634 | gridSizer.Add(wx.StaticText(tab, -1, _("Filename:")), flag=wx.RIGHT, border=HALF_SPACE, row=0, col=0) | |
1635 | if os.path.isfile(filename): | |
1636 | gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1]), row=0, col=1) | |
1637 | ||
1638 | gridSizer.Add(wx.StaticText(tab, -1, _("Location:")), flag=wx.RIGHT, border=HALF_SPACE, row=1, col=0) | |
1639 | gridSizer.Add(wx.StaticText(tab, -1, filePropertiesService.chopPath(os.path.split(filename)[0])), flag=wx.BOTTOM, border=SPACE, row=1, col=1) | |
1640 | ||
1641 | gridSizer.Add(wx.StaticText(tab, -1, _("Size:")), flag=wx.RIGHT, border=HALF_SPACE, row=2, col=0) | |
1642 | gridSizer.Add(wx.StaticText(tab, -1, str(os.path.getsize(filename)) + ' ' + _("bytes")), row=2, col=1) | |
1643 | ||
1644 | lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion | |
1645 | lineSizer.Add(wx.StaticLine(tab, -1, size = (10,-1)), 0, wx.EXPAND) | |
1646 | gridSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.TOP, border=HALF_SPACE, row=3, col=0, colspan=2) | |
1647 | ||
1648 | gridSizer.Add(wx.StaticText(tab, -1, _("Created:")), flag=wx.RIGHT, border=HALF_SPACE, row=4, col=0) | |
1649 | gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getctime(filename))), row=4, col=1) | |
1650 | ||
1651 | gridSizer.Add(wx.StaticText(tab, -1, _("Modified:")), flag=wx.RIGHT, border=HALF_SPACE, row=5, col=0) | |
1652 | gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getmtime(filename))), row=5, col=1) | |
1653 | ||
1654 | gridSizer.Add(wx.StaticText(tab, -1, _("Accessed:")), flag=wx.RIGHT, border=HALF_SPACE, row=6, col=0) | |
1655 | gridSizer.Add(wx.StaticText(tab, -1, time.ctime(os.path.getatime(filename))), row=6, col=1) | |
1656 | ||
1657 | else: | |
1658 | gridSizer.Add(wx.StaticText(tab, -1, os.path.split(filename)[1] + ' ' + _("[new project]")), row=0, col=1) | |
1659 | ||
1660 | # add a border around the inside of the tab | |
1661 | spacerGrid = wx.BoxSizer(wx.VERTICAL) | |
1662 | spacerGrid.Add(gridSizer, 0, wx.ALL, SPACE); | |
1663 | tab.SetSizer(spacerGrid) | |
1664 | notebook.AddPage(tab, _("General")) | |
1665 | if wx.Platform == "__WXMSW__": | |
1666 | notebook.SetPageSize((310,200)) | |
1667 | ||
1668 | sizer = wx.BoxSizer(wx.VERTICAL) | |
1669 | sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE) | |
1670 | sizer.Add(self.CreateButtonSizer(wx.OK), 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, HALF_SPACE) | |
1671 | ||
1f780e48 | 1672 | self.SetSizer(sizer) |
6f1a3f9c | 1673 | sizer.Fit(self) |
1f780e48 RD |
1674 | self.Layout() |
1675 | ||
1676 | ||
1677 | class ProjectOptionsPanel(wx.Panel): | |
1678 | ||
1679 | ||
1680 | def __init__(self, parent, id): | |
1681 | wx.Panel.__init__(self, parent, id) | |
1682 | self._useSashMessageShown = False | |
1f780e48 RD |
1683 | config = wx.ConfigBase_Get() |
1684 | self._projSaveDocsCheckBox = wx.CheckBox(self, -1, _("Remember open projects")) | |
1685 | self._projSaveDocsCheckBox.SetValue(config.ReadInt("ProjectSaveDocs", True)) | |
1686 | projectBorderSizer = wx.BoxSizer(wx.VERTICAL) | |
1687 | projectSizer = wx.BoxSizer(wx.VERTICAL) | |
1688 | projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE) | |
bbf7159c RD |
1689 | if not ACTIVEGRID_BASE_IDE: |
1690 | self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog")) | |
1691 | self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog", True)) | |
1692 | projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE) | |
1f780e48 RD |
1693 | projectBorderSizer.Add(projectSizer, 0, wx.ALL, SPACE) |
1694 | self.SetSizer(projectBorderSizer) | |
1695 | self.Layout() | |
1696 | parent.AddPage(self, _("Project")) | |
1697 | ||
1698 | def OnUseSashSelect(self, event): | |
1699 | if not self._useSashMessageShown: | |
1700 | msgTitle = wx.GetApp().GetAppName() | |
1701 | if not msgTitle: | |
1702 | msgTitle = _("Document Options") | |
1703 | wx.MessageBox("Project window embedded mode changes will not appear until the application is restarted.", | |
1704 | msgTitle, | |
1705 | wx.OK | wx.ICON_INFORMATION, | |
1706 | self.GetParent()) | |
1707 | self._useSashMessageShown = True | |
1708 | ||
1709 | ||
1710 | def OnOK(self, optionsDialog): | |
1711 | config = wx.ConfigBase_Get() | |
1712 | config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue()) | |
bbf7159c RD |
1713 | if not ACTIVEGRID_BASE_IDE: |
1714 | config.WriteInt("RunWelcomeDialog", self._projShowWelcomeCheckBox.GetValue()) | |
1f780e48 RD |
1715 | |
1716 | ||
1717 | class ProjectService(Service.Service): | |
1718 | ||
1719 | #---------------------------------------------------------------------------- | |
1720 | # Constants | |
1721 | #---------------------------------------------------------------------------- | |
1722 | SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service | |
1723 | RUNPM_ID = wx.NewId() | |
1724 | RUN_SELECTED_PM_ID = wx.NewId() | |
1725 | RUN_CURRENT_PM_ID = wx.NewId() | |
1726 | ADD_FILES_TO_PROJECT_ID = wx.NewId() | |
1727 | ADD_CURRENT_FILE_TO_PROJECT_ID = wx.NewId() | |
1728 | RENAME_ID = wx.NewId() | |
1729 | OPEN_SELECTION_ID = wx.NewId() | |
1730 | REMOVE_FROM_PROJECT = wx.NewId() | |
b792147d RD |
1731 | DELETE_FILE_ID = wx.NewId() |
1732 | ADD_ALL_FILES_TO_PROJECT_ID = wx.NewId() | |
1733 | ||
1f780e48 RD |
1734 | |
1735 | #---------------------------------------------------------------------------- | |
1736 | # Overridden methods | |
1737 | #---------------------------------------------------------------------------- | |
1738 | ||
1739 | def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_LEFT): | |
1740 | Service.Service.__init__(self, serviceName, embeddedWindowLocation) | |
1741 | self._runHandlers = [] | |
1742 | self._suppressOpenProjectMessages = False | |
1743 | ||
1744 | ||
1745 | def _CreateView(self): | |
1746 | return ProjectView(self) | |
1747 | ||
1748 | ||
1749 | def ShowWindow(self, show = True): | |
1750 | """ Force showing of saved projects on opening, otherwise empty Project Window is disconcerting for user """ | |
1751 | Service.Service.ShowWindow(self, show) | |
1752 | ||
1753 | if show: | |
1754 | project = self.GetView().GetDocument() | |
1755 | if not project: | |
1756 | self.OpenSavedProjects() | |
1757 | ||
1758 | ||
1759 | #---------------------------------------------------------------------------- | |
1760 | # Service specific methods | |
1761 | #---------------------------------------------------------------------------- | |
1762 | ||
1763 | def GetSuppressOpenProjectMessages(self): | |
1764 | return self._suppressOpenProjectMessages | |
1765 | ||
1766 | ||
1767 | def SetSuppressOpenProjectMessages(self, suppressOpenProjectMessages): | |
1768 | self._suppressOpenProjectMessages = suppressOpenProjectMessages | |
1769 | ||
1770 | ||
1771 | def GetRunHandlers(self): | |
1772 | return self._runHandlers | |
1773 | ||
1774 | ||
1775 | def AddRunHandler(self, runHandler): | |
1776 | self._runHandlers.append(runHandler) | |
1777 | ||
1778 | ||
1779 | def RemoveRunHandler(self, runHandler): | |
1780 | self._runHandlers.remove(runHandler) | |
1781 | ||
1782 | ||
1783 | def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None): | |
1784 | Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document) | |
1785 | ||
1f780e48 RD |
1786 | projectMenu = wx.Menu() |
1787 | ||
1788 | ## accelTable = wx.AcceleratorTable([ | |
1789 | ## eval(_("wx.ACCEL_CTRL, ord('R'), ProjectService.RUN_ID")) | |
1790 | ## ]) | |
1791 | ## frame.SetAcceleratorTable(accelTable) | |
1792 | isProjectDocument = document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument | |
1793 | if wx.GetApp().IsMDI() or isProjectDocument: | |
1794 | if not menuBar.FindItemById(ProjectService.ADD_FILES_TO_PROJECT_ID): | |
b792147d | 1795 | projectMenu.Append(ProjectService.ADD_FILES_TO_PROJECT_ID, _("Add &Files to Project..."), _("Adds a document to the current project")) |
1f780e48 RD |
1796 | wx.EVT_MENU(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessEvent) |
1797 | wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent) | |
b792147d | 1798 | if not menuBar.FindItemById(ProjectService.ADD_ALL_FILES_TO_PROJECT_ID): |
6f1a3f9c | 1799 | 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 |
1800 | wx.EVT_MENU(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessEvent) |
1801 | wx.EVT_UPDATE_UI(frame, ProjectService.ADD_ALL_FILES_TO_PROJECT_ID, frame.ProcessUpdateUIEvent) | |
1f780e48 RD |
1802 | if not menuBar.FindItemById(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID): |
1803 | projectMenu.Append(ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, _("&Add Active File to Project..."), _("Adds the active document to a project")) | |
1804 | wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent) | |
1805 | wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent) | |
1806 | viewMenuIndex = menuBar.FindMenu(_("&View")) | |
1807 | menuBar.Insert(viewMenuIndex + 1, projectMenu, _("&Project")) | |
1808 | editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit"))) | |
1809 | if not menuBar.FindItemById(ProjectService.RENAME_ID): | |
1f780e48 RD |
1810 | editMenu.Append(ProjectService.RENAME_ID, _("&Rename"), _("Renames the active item")) |
1811 | wx.EVT_MENU(frame, ProjectService.RENAME_ID, frame.ProcessEvent) | |
1812 | wx.EVT_UPDATE_UI(frame, ProjectService.RENAME_ID, frame.ProcessUpdateUIEvent) | |
b792147d RD |
1813 | if not menuBar.FindItemById(ProjectService.DELETE_FILE_ID): |
1814 | editMenu.Append(ProjectService.DELETE_FILE_ID, _("Delete File"), _("Delete the file from the project and file system.")) | |
1815 | wx.EVT_MENU(frame, ProjectService.DELETE_FILE_ID, frame.ProcessEvent) | |
1816 | wx.EVT_UPDATE_UI(frame, ProjectService.DELETE_FILE_ID, frame.ProcessUpdateUIEvent) | |
1f780e48 RD |
1817 | |
1818 | return True | |
1819 | ||
1820 | ||
1821 | def OnCloseFrame(self, event): | |
1822 | if not self.GetView(): | |
1823 | return True | |
1824 | ||
1825 | if wx.GetApp().IsMDI(): | |
1826 | # close all non-project documents first | |
1827 | for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted | |
1828 | if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument: | |
1829 | if not self.GetDocumentManager().CloseDocument(document, False): | |
1830 | return False | |
1831 | ||
1832 | # write project config afterwards because user may change filenames on closing of new documents | |
1833 | self.GetView().WriteProjectConfig() # Called onCloseWindow in all of the other services but needed to be factored out for ProjectService since it is called elsewhere | |
1834 | ||
1835 | # close all project documents after closing other documents | |
1836 | # because user may save a new document with a new name or cancel closing a document | |
1837 | for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted | |
1838 | if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
1839 | if not document.OnSaveModified(): | |
1840 | return False | |
1841 | ||
1842 | # This is called when any SDI frame is closed, so need to check if message window is closing or some other window | |
1843 | elif self.GetView() == event.GetEventObject().GetView(): | |
1844 | self.SetView(None) | |
1845 | return True | |
1846 | ||
1847 | ||
1848 | #---------------------------------------------------------------------------- | |
1849 | # Event Processing Methods | |
1850 | #---------------------------------------------------------------------------- | |
1851 | ||
1852 | def ProcessEventBeforeWindows(self, event): | |
1853 | id = event.GetId() | |
26ee3a06 | 1854 | |
1f780e48 RD |
1855 | if id == wx.ID_CLOSE_ALL: |
1856 | self.OnFileCloseAll(event) | |
1857 | return True | |
26ee3a06 RD |
1858 | |
1859 | elif id == wx.ID_CLOSE: | |
1860 | document = self.GetDocumentManager().GetCurrentDocument() | |
1861 | if document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
1862 | self.OnProjectClose(event) | |
1863 | return True | |
1864 | else: | |
1865 | return False | |
1866 | return False | |
1867 | ||
1868 | ||
1869 | def ProcessUpdateUIEventBeforeWindows(self, event): | |
1870 | id = event.GetId() | |
1871 | ||
1872 | if id == wx.ID_CLOSE_ALL: | |
1873 | for document in self.GetDocumentManager().GetDocuments(): | |
1874 | if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument: | |
1875 | event.Enable(True) | |
1876 | return True | |
1877 | ||
1878 | event.Enable(False) | |
1879 | return True | |
1880 | ||
1881 | elif id == wx.ID_CLOSE: | |
1882 | document = self.GetDocumentManager().GetCurrentDocument() | |
1883 | if document and document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
1884 | projectFilenames = self.GetView().GetSelectedProjects() | |
1885 | if projectFilenames and len(projectFilenames): | |
1886 | event.Enable(True) | |
1887 | else: | |
1888 | event.Enable(False) | |
1889 | return True | |
1890 | ||
1f780e48 RD |
1891 | return False |
1892 | ||
1893 | ||
1894 | def ProcessEvent(self, event): | |
1895 | if Service.Service.ProcessEvent(self, event): | |
1896 | return True | |
1897 | ||
1898 | id = event.GetId() | |
1899 | if id == ProjectService.RUN_SELECTED_PM_ID: | |
1900 | self.OnRunProcessModel(event, runSelected=True) | |
1901 | return True | |
1902 | elif id == ProjectService.RUN_CURRENT_PM_ID: | |
1903 | self.OnRunProcessModel(event, runCurrentFile=True) | |
1904 | return True | |
1905 | elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID: | |
1906 | self.OnAddCurrentFileToProject(event) | |
1907 | return True | |
1908 | elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID: | |
1909 | if self.GetView(): | |
1910 | return self.GetView().ProcessEvent(event) | |
1911 | else: | |
1912 | return False | |
1913 | else: | |
1914 | return False | |
1915 | ||
1916 | ||
1917 | def ProcessUpdateUIEvent(self, event): | |
1918 | if Service.Service.ProcessUpdateUIEvent(self, event): | |
1919 | return True | |
1920 | ||
1921 | id = event.GetId() | |
b792147d RD |
1922 | if (id == ProjectService.RUNPM_ID |
1923 | or id == ProjectService.RUN_SELECTED_PM_ID | |
1924 | or id == ProjectService.RUN_CURRENT_PM_ID): | |
1f780e48 RD |
1925 | event.Enable(self._HasOpenedProjects() and self._HasProcessModel()) |
1926 | return True | |
1f780e48 RD |
1927 | elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID: |
1928 | event.Enable(self._CanAddCurrentFileToProject()) | |
1929 | return True | |
b792147d RD |
1930 | elif (id == ProjectService.ADD_FILES_TO_PROJECT_ID |
1931 | or id == ProjectService.ADD_ALL_FILES_TO_PROJECT_ID | |
1932 | or id == ProjectService.RENAME_ID | |
1933 | or id == ProjectService.OPEN_SELECTION_ID | |
1934 | or id == ProjectService.DELETE_FILE_ID): | |
1f780e48 RD |
1935 | event.Enable(False) |
1936 | return True | |
1937 | elif id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID: | |
1938 | if self.GetView(): | |
1939 | return self.GetView().ProcessUpdateUIEvent(event) | |
1940 | else: | |
1941 | return False | |
1942 | else: | |
1943 | return False | |
1944 | ||
1945 | ||
1946 | def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False): | |
1947 | project = self.GetView().GetDocument() | |
1948 | ||
1949 | if project: | |
1950 | ext = None | |
1951 | for template in self.GetDocumentManager().GetTemplates(): | |
1952 | if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument: | |
1953 | ext = template.GetDefaultExtension() | |
1954 | break; | |
1955 | if not ext: | |
1956 | return | |
1957 | ||
1958 | files = filter(lambda f: f.endswith(ext), project.GetFiles()) | |
1959 | if not files: | |
1960 | return | |
1961 | ||
1962 | docs = wx.GetApp().GetDocumentManager().GetDocuments() | |
1963 | for doc in docs: | |
1964 | if doc.GetFilename() in files and doc.GetDocumentTemplate().GetDocumentType() == ProcessModelEditor.ProcessModelDocument: | |
1965 | if not doc.GetProcessModel().beginProcess: | |
1966 | wx.MessageBox(_("Cannot run process. No begin action found."), _("Run Process")) | |
1967 | return | |
1968 | ||
1969 | filesModified = False | |
1970 | for doc in docs: | |
1971 | if doc.IsModified(): | |
1972 | filesModified = True | |
1973 | break | |
1974 | if filesModified: | |
1975 | frame = self.GetView().GetFrame() | |
1976 | yesNoMsg = wx.MessageDialog(frame, | |
1977 | _("Files have been modified. Process may not reflect your current changes.\n\nWould you like to save all files before running?"), | |
1978 | _("Run Process"), | |
b792147d | 1979 | wx.YES_NO|wx.ICON_QUESTION |
1f780e48 RD |
1980 | ) |
1981 | if yesNoMsg.ShowModal() == wx.ID_YES: | |
1982 | wx.GetTopLevelParent(frame).OnFileSaveAll(None) | |
1983 | ||
1984 | if runCurrentFile: | |
1985 | fileToRun = self.GetDocumentManager().GetCurrentDocument().GetFilename() | |
1986 | elif runSelected: | |
1987 | fileToRun = self.GetView().GetSelectedFile() | |
1988 | elif len(files) > 1: | |
1989 | files.sort(lambda a, b: cmp(os.path.basename(a).lower(), os.path.basename(b).lower())) | |
1990 | strings = map(lambda file: os.path.basename(file), files) | |
1991 | res = wx.GetSingleChoiceIndex(_("Select a process to run:"), | |
1992 | _("Run"), | |
1993 | strings, | |
1994 | project.GetFirstView()._GetParentFrame()) | |
1995 | if res == -1: | |
1996 | return | |
1997 | fileToRun = files[res] | |
1998 | else: | |
1999 | fileToRun = files[0] | |
2000 | ||
2001 | self.RunProcessModel(fileToRun) | |
2002 | ||
2003 | ||
2004 | def RunProcessModel(self, fileToRun): | |
2005 | for runHandler in self.GetRunHandlers(): | |
2006 | if runHandler.RunProjectFile(fileToRun): | |
2007 | return | |
2008 | os.system('"' + fileToRun + '"') | |
2009 | ||
2010 | ||
2011 | def _HasProcessModel(self): | |
2012 | project = self.GetView().GetDocument() | |
2013 | ||
2014 | if project: | |
2015 | ext = None | |
2016 | for template in self.GetDocumentManager().GetTemplates(): | |
2017 | if template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument: | |
2018 | ext = template.GetDefaultExtension() | |
2019 | break; | |
2020 | if not ext: | |
2021 | return False | |
2022 | ||
2023 | files = filter(lambda f: f.endswith(ext), project.GetFiles()) | |
2024 | if not files: | |
2025 | return False | |
2026 | ||
2027 | if len(files): | |
2028 | return True | |
2029 | ||
2030 | return False | |
2031 | ||
2032 | ||
2033 | def _HasOpenedProjects(self): | |
2034 | for document in self.GetDocumentManager().GetDocuments(): | |
2035 | if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
2036 | return True | |
2037 | return False | |
2038 | ||
2039 | ||
2040 | def _HasCurrentFile(self): | |
2041 | currentDoc = self.GetDocumentManager().GetCurrentDocument() | |
2042 | return currentDoc | |
2043 | ||
2044 | ||
2045 | def _CanAddCurrentFileToProject(self): | |
2046 | currentDoc = self.GetDocumentManager().GetCurrentDocument() | |
2047 | if not currentDoc: | |
2048 | return False | |
2049 | if currentDoc.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
2050 | return False | |
2051 | if not currentDoc._savedYet: | |
2052 | return False | |
2053 | for document in self.GetDocumentManager().GetDocuments(): | |
2054 | if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
2055 | return True | |
2056 | return False # There are no documents open | |
2057 | ||
2058 | ||
2059 | def GetFilesFromCurrentProject(self): | |
2060 | view = self.GetView() | |
2061 | if view: | |
2062 | project = view.GetDocument() | |
2063 | if project: | |
2064 | return project.GetFiles() | |
2065 | return None | |
2066 | ||
2067 | ||
2068 | def GetCurrentProject(self): | |
2069 | view = self.GetView() | |
2070 | if view: | |
2071 | return view.GetDocument() | |
2072 | return None | |
2073 | ||
2074 | ||
2075 | def FindProjectByFile(self, filename): | |
2076 | for document in self.GetDocumentManager().GetDocuments(): | |
2077 | if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
2078 | if document.GetFilename() == filename: | |
2079 | return document | |
2080 | elif document.IsFileInProject(filename): | |
2081 | return document | |
2082 | return None | |
2083 | ||
2084 | ||
2085 | def GetCurrentProjectNames(self): | |
2086 | projects = [] | |
2087 | for document in self.GetDocumentManager().GetDocuments(): | |
2088 | if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
2089 | projects.append(document) | |
2090 | if not projects: | |
2091 | return | |
2092 | projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower())) | |
2093 | strings = map(lambda project: project.GetPrintableName(), projects) | |
2094 | return strings | |
2095 | ||
b792147d | 2096 | |
1f780e48 RD |
2097 | def OnAddCurrentFileToProject(self, event): |
2098 | if not self._CanAddCurrentFileToProject(): | |
2099 | return | |
2100 | projects = [] | |
2101 | for document in self.GetDocumentManager().GetDocuments(): | |
2102 | if document.GetDocumentTemplate().GetDocumentType() == ProjectDocument: | |
2103 | projects.append(document) | |
2104 | if not projects: | |
2105 | return | |
2106 | projects.sort(lambda a, b: cmp(a.GetPrintableName().lower(), b.GetPrintableName().lower())) | |
2107 | strings = map(lambda project: project.GetPrintableName(), projects) | |
2108 | res = wx.GetSingleChoiceIndex(_("Select a project to add the file to:"), | |
2109 | _("Add to Project"), | |
2110 | strings, | |
2111 | self.GetDocumentManager().FindSuitableParent()) | |
2112 | if res == -1: | |
2113 | return | |
2114 | file = self.GetDocumentManager().GetCurrentDocument().GetFilename() | |
2115 | projects[res].GetCommandProcessor().Submit(ProjectAddFilesCommand(projects[res], [file])) | |
2116 | self.GetView().Activate(True) # after add, should put focus on project editor | |
2117 | ||
2118 | ||
26ee3a06 RD |
2119 | def OnProjectClose(self, event): |
2120 | projectFilenames = self.GetView().GetSelectedProjects() | |
2121 | for filename in projectFilenames: | |
2122 | doc = self.FindProjectByFile(filename) | |
2123 | if doc: | |
2124 | self.GetDocumentManager().CloseDocument(doc, False) | |
2125 | ||
2126 | ||
1f780e48 RD |
2127 | def OnFileCloseAll(self, event): |
2128 | for document in self.GetDocumentManager().GetDocuments()[:]: # Cloning list to make sure we go through all docs even as they are deleted | |
2129 | if document.GetDocumentTemplate().GetDocumentType() != ProjectDocument: | |
2130 | if not self.GetDocumentManager().CloseDocument(document, False): | |
2131 | return | |
2132 | # document.DeleteAllViews() # Implicitly delete the document when the last view is removed | |
2133 | ||
2134 | ||
2135 | def OpenSavedProjects(self): | |
2136 | config = wx.ConfigBase_Get() | |
2137 | openedDocs = False | |
2138 | if config.ReadInt("ProjectSaveDocs", True): | |
2139 | docString = config.Read("ProjectSavedDocs") | |
2140 | if docString: | |
2141 | doc = None | |
2142 | for fileName in eval(docString): | |
2143 | if isinstance(fileName, types.StringTypes): | |
2144 | if os.path.exists(fileName): | |
2145 | doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT) | |
2146 | ||
2147 | if doc: | |
2148 | openedDocs = True | |
2149 | expandedString = config.Read("ProjectExpandedSavedDocs") | |
2150 | if expandedString: | |
2151 | view = doc.GetFirstView() | |
2152 | view.SetExpandedProjects(eval(expandedString)) | |
2153 | return openedDocs | |
2154 | ||
b792147d RD |
2155 | |
2156 | class ProjectEditorMoveCommand(wx.lib.docview.Command): | |
2157 | ||
2158 | def __init__(self, view, newPositionItem, item): | |
2159 | wx.lib.docview.Command.__init__(self, canUndo = True) | |
2160 | self._view = view | |
2161 | self._item = item | |
2162 | self._file = view._treeCtrl.GetLongFilename(item) | |
2163 | if view._IsItemFile(item): | |
2164 | self._projectOld = view._GetItemProject(item) | |
2165 | else: # view._IsItemProject(item): | |
2166 | self._projectOld = None | |
2167 | self._projectNew = view._GetItemProject(newPositionItem) | |
2168 | ||
2169 | ||
2170 | def GetName(self): | |
2171 | return _("Move File %s") % os.path.basename(self._file) | |
2172 | ||
2173 | ||
2174 | def Do(self): | |
2175 | if self._projectOld: | |
2176 | self._projectOld.RemoveFile(self._file) | |
2177 | if self._projectNew: | |
2178 | self._projectNew.AddFile(self._file) | |
2179 | return True | |
2180 | ||
2181 | ||
2182 | def Undo(self): | |
2183 | if self._projectNew: | |
2184 | self._projectNew.RemoveFile(self._file) | |
2185 | if self._projectOld: | |
2186 | self._projectOld.AddFile(self._file) | |
2187 | return True | |
2188 | ||
2189 | ||
1f780e48 RD |
2190 | #---------------------------------------------------------------------------- |
2191 | # Icon Bitmaps - generated by encode_bitmaps.py | |
2192 | #---------------------------------------------------------------------------- | |
2193 | from wx import ImageFromStream, BitmapFromImage | |
1f780e48 RD |
2194 | import cStringIO |
2195 | ||
2196 | ||
2197 | def getProjectData(): | |
2198 | return \ | |
2199 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ | |
2200 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
2201 | \x00\x00[IDAT8\x8d\xc5\x93\xc1\n\xc00\x08C\x8d\xf6\xff\xffX\xb3Sa-\xf6`;:O\n\ | |
2202 | \x12\x1fj\x0059\t\xed\t\xc3\xc9pn\x0b\x88\x88@\rU\x81\xf6.\x18N\xa8aE\x92\rh\ | |
2203 | YC\x85\xa4D\x90\x91\xdc%\xf8w\x07+\xd1\xfbW\x98\xc5\x8f\t\x86W\xee\x93+\xbe\ | |
2204 | \xc0gn\xdc\x8d\x07\xab"<iG\x8e\xa9\r\x00\x00\x00\x00IEND\xaeB`\x82' | |
2205 | ||
2206 | def getProjectBitmap(): | |
2207 | return BitmapFromImage(getProjectImage()) | |
2208 | ||
2209 | def getProjectImage(): | |
2210 | stream = cStringIO.StringIO(getProjectData()) | |
2211 | return ImageFromStream(stream) | |
2212 | ||
2213 | def getProjectIcon(): | |
bbf7159c | 2214 | return wx.IconFromBitmap(getProjectBitmap()) |
1f780e48 RD |
2215 | |
2216 | ||
2217 | #---------------------------------------------------------------------------- | |
2218 | ||
2219 | def getBlankData(): | |
2220 | return \ | |
bbf7159c RD |
2221 | '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\ |
2222 | \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\ | |
2223 | \x00\x00]IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\x87\xb6C\x11\x89\ | |
2224 | \xa8X:\xd4\x13\x03:\x1b\x01\xa45T\xd4\xefBsh\xd7Hk\xdc\x02\x00@\x8a\x19$\xa1\ | |
2225 | 9\x14A,\x95\xf3\x82G)\xd3\x00\xf24\xf7\x90\x1ev\x07\xee\x1e\xf4:\xc1J?\xe0\ | |
2226 | \x0b\x80\xc7\x1d\xf8\x1dg\xc4\xea7\x96G8\x00\xa8\x91\x19(\x85#P\x7f\x00\x00\ | |
2227 | \x00\x00IEND\xaeB`\x82' | |
1f780e48 RD |
2228 | |
2229 | ||
2230 | def getBlankBitmap(): | |
2231 | return BitmapFromImage(getBlankImage()) | |
2232 | ||
2233 | def getBlankImage(): | |
2234 | stream = cStringIO.StringIO(getBlankData()) | |
2235 | return ImageFromStream(stream) | |
2236 | ||
2237 | def getBlankIcon(): | |
bbf7159c | 2238 | return wx.IconFromBitmap(getBlankBitmap()) |
1f780e48 | 2239 |