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