55 from eric7.Tasks.TasksFile import TasksFile |
55 from eric7.Tasks.TasksFile import TasksFile |
56 from eric7.UI import Config |
56 from eric7.UI import Config |
57 from eric7.UI.NotificationWidget import NotificationTypes |
57 from eric7.UI.NotificationWidget import NotificationTypes |
58 |
58 |
59 from .DebuggerPropertiesFile import DebuggerPropertiesFile |
59 from .DebuggerPropertiesFile import DebuggerPropertiesFile |
|
60 from .FileCategoryRepositoryItem import FileCategoryRepositoryItem |
60 from .ProjectBrowserModel import ProjectBrowserModel |
61 from .ProjectBrowserModel import ProjectBrowserModel |
61 from .ProjectFile import ProjectFile |
62 from .ProjectFile import ProjectFile |
62 from .UserProjectFile import UserProjectFile |
63 from .UserProjectFile import UserProjectFile |
63 |
64 |
64 |
65 |
211 self.applicationDiagram = None |
217 self.applicationDiagram = None |
212 self.loadedDiagram = None |
218 self.loadedDiagram = None |
213 self.__findProjectFileDialog = None |
219 self.__findProjectFileDialog = None |
214 |
220 |
215 self.processChangedProjectFiles.connect(self.__autoExecuteMake) |
221 self.processChangedProjectFiles.connect(self.__autoExecuteMake) |
|
222 |
|
223 def addFileCategory(self, category, categoryItem): |
|
224 """ |
|
225 Public method to add a file category to the categories repository. |
|
226 |
|
227 Note: The given category must not be contained in the repository already. |
|
228 |
|
229 @param category file category (must be unique) |
|
230 @type str |
|
231 @param categoryItem data class instance containing the category data |
|
232 @type FileCategoryRepositoryItem |
|
233 @exception TypeError raised to signal a wrong type for the category item |
|
234 """ |
|
235 if not isinstance(categoryItem, FileCategoryRepositoryItem): |
|
236 raise TypeError( |
|
237 "'categoryItem' must be an instance of 'FileCategoryRepositoryItem'." |
|
238 ) |
|
239 |
|
240 if category in self.__fileCategoriesRepository: |
|
241 EricMessageBox.critical( |
|
242 self.ui, |
|
243 self.tr("Add File Category"), |
|
244 self.tr( |
|
245 "<p>The file category <b>{0}</b> has already been added. This" |
|
246 " attempt will be ignored.</p>" |
|
247 ), |
|
248 ) |
|
249 else: |
|
250 self.__fileCategoriesRepository[category] = categoryItem |
|
251 with contextlib.suppress(AttributeError): |
|
252 self.__pdata[category] = [] |
|
253 |
|
254 def removeFileCategory(self, category): |
|
255 """ |
|
256 Public method to remove a category from the categories repository. |
|
257 |
|
258 Note: If the category is not contained in the repository, the request to |
|
259 remove it will be ignored silently. |
|
260 |
|
261 @param category file category |
|
262 @type str |
|
263 """ |
|
264 with contextlib.suppress(KeyError): |
|
265 del self.__fileCategoriesRepository[category] |
216 |
266 |
217 def __sourceExtensions(self, language): |
267 def __sourceExtensions(self, language): |
218 """ |
268 """ |
219 Private method to get the source extensions of a programming language. |
269 Private method to get the source extensions of a programming language. |
220 |
270 |
463 } |
513 } |
464 |
514 |
465 self.__pdata = { |
515 self.__pdata = { |
466 "DESCRIPTION": "", |
516 "DESCRIPTION": "", |
467 "VERSION": "", |
517 "VERSION": "", |
468 "SOURCES": [], |
|
469 "FORMS": [], |
|
470 "RESOURCES": [], |
|
471 "INTERFACES": [], |
|
472 "PROTOCOLS": [], |
|
473 "OTHERS": [], |
|
474 "TRANSLATIONS": [], |
|
475 "TRANSLATIONEXCEPTIONS": [], |
518 "TRANSLATIONEXCEPTIONS": [], |
476 "TRANSLATIONPATTERN": "", |
519 "TRANSLATIONPATTERN": "", |
477 "TRANSLATIONSBINPATH": "", |
520 "TRANSLATIONSBINPATH": "", |
478 "TRANSLATIONSOURCESTARTPATH": "", |
521 "TRANSLATIONSOURCESTARTPATH": "", |
479 "MAINSCRIPT": "", |
522 "MAINSCRIPT": "", |
524 "DOCSTRING": "", |
567 "DOCSTRING": "", |
525 "TESTING_FRAMEWORK": "", |
568 "TESTING_FRAMEWORK": "", |
526 "LICENSE": "", |
569 "LICENSE": "", |
527 "EMBEDDED_VENV": False, |
570 "EMBEDDED_VENV": False, |
528 } |
571 } |
529 # TODO: Move these to a file categories repository populated through the |
572 for category in self.__fileCategoriesRepository: |
530 # project browsers |
573 self.__pdata[category] = [] |
531 self.__knownFileCategories = [ |
|
532 "FORMS", |
|
533 "OTHERS", |
|
534 "RESOURCES", |
|
535 "SOURCES", |
|
536 "TRANSLATIONS", |
|
537 "INTERFACES", |
|
538 "PROTOCOLS", |
|
539 ] |
|
540 self.__fileCategoryFilterTemplates = { |
|
541 "FORMS": self.tr("Form Files ({0})"), |
|
542 "OTHERS": self.tr("Other Files ({0})"), |
|
543 "RESOURCES": self.tr("Resource Files ({0})"), |
|
544 "SOURCES": self.tr("Source Files ({0})"), |
|
545 "TRANSLATIONS": self.tr("Translation Files ({0})"), |
|
546 "INTERFACES": self.tr("Interface Files ({0})"), |
|
547 "PROTOCOLS": self.tr("Protocol Files ({0})"), |
|
548 } |
|
549 self.__fileCategoryUserStrings = { |
|
550 "FORMS": self.tr("Form Files"), |
|
551 "OTHERS": self.tr("Other Files"), |
|
552 "RESOURCES": self.tr("Resource Files"), |
|
553 "SOURCES": self.tr("Source Files"), |
|
554 "TRANSLATIONS": self.tr("Translation Files"), |
|
555 "INTERFACES": self.tr("Interface Files"), |
|
556 "PROTOCOLS": self.tr("Protocol Files"), |
|
557 } |
|
558 self.__fileCategoryTyeStrings = { |
|
559 "FORMS": self.tr("Forms"), |
|
560 "OTHERS": self.tr("Others"), |
|
561 "RESOURCES": self.tr("Resources"), |
|
562 "SOURCES": self.tr("Sources"), |
|
563 "TRANSLATIONS": self.tr("Translations"), |
|
564 "INTERFACES": self.tr("Interfaces"), |
|
565 "PROTOCOLS": self.tr("Protocols"), |
|
566 } |
|
567 self.__fileCategoryExtensions = { |
|
568 "FORMS": ["*.ui"], |
|
569 "OTHERS": [], |
|
570 "RESOURCES": ["*.qrc"], |
|
571 "SOURCES": ["*.py", "*.pyw"], # Python files as default |
|
572 "TRANSLATIONS": ["*.ts", "*.qm"], |
|
573 "INTERFACES": ["*.idl"], |
|
574 "PROTOCOLS": ["*.proto"], |
|
575 } |
|
576 # until here |
|
577 |
574 |
578 self.__initDebugProperties() |
575 self.__initDebugProperties() |
579 |
576 |
580 self.pudata = { |
577 self.pudata = { |
581 "VCSOVERRIDE": "", |
578 "VCSOVERRIDE": "", |
703 Public method to get the list of known file categories. |
700 Public method to get the list of known file categories. |
704 |
701 |
705 @return list of known file categories |
702 @return list of known file categories |
706 @rtype list of str |
703 @rtype list of str |
707 """ |
704 """ |
708 return self.__knownFileCategories[:] |
705 return list(self.__fileCategoriesRepository.keys()) |
709 |
706 |
710 def getFileCategoryFilterString( |
707 def getFileCategoryFilterString( |
711 self, categories=None, withOthers=False, withAll=True |
708 self, categories=None, withOthers=False, withAll=True |
712 ): |
709 ): |
713 """ |
710 """ |
724 @type bool (optional) |
721 @type bool (optional) |
725 @return file selection filter string |
722 @return file selection filter string |
726 @rtype str |
723 @rtype str |
727 """ |
724 """ |
728 if categories is None: |
725 if categories is None: |
729 categories = [c for c in self.__knownFileCategories if c != "OTHERS"] |
726 categories = [c for c in self.__fileCategoriesRepository if c != "OTHERS"] |
730 if withOthers: |
727 if withOthers: |
731 categories.append("OTHERS") |
728 categories.append("OTHERS") |
732 |
729 |
733 patterns = collections.defaultdict(list) |
730 patterns = collections.defaultdict(list) |
734 for pattern, filetype in self.__pdata["FILETYPES"].items(): |
731 for pattern, filetype in self.__pdata["FILETYPES"].items(): |
735 if filetype in categories and filetype in self.__knownFileCategories: |
732 if filetype in categories and filetype in self.__fileCategoriesRepository: |
736 patterns[filetype].append(pattern) |
733 patterns[filetype].append(pattern) |
737 |
734 |
738 filters = [] |
735 filters = [] |
739 for filetype in patterns: |
736 for filetype in patterns: |
740 filters.append( |
737 filters.append( |
741 self.__fileCategoryFilterTemplates[filetype].format( |
738 self.__fileCategoriesRepository[ |
|
739 filetype |
|
740 ].fileCategoryFilterTemplate.format( |
742 " ".join(sorted(patterns[filetype])) |
741 " ".join(sorted(patterns[filetype])) |
743 ) |
742 ) |
744 ) |
743 ) |
745 filterString = ";;".join(sorted(filters)) |
744 filterString = ";;".join(sorted(filters)) |
746 if withAll: |
745 if withAll: |
755 @param category file type category |
754 @param category file type category |
756 @type str |
755 @type str |
757 @return user string for the category |
756 @return user string for the category |
758 @rtype str |
757 @rtype str |
759 """ |
758 """ |
760 return self.__fileCategoryUserStrings[category] |
759 return self.__fileCategoriesRepository[category].fileCategoryUserString |
761 |
760 |
762 def getFileCategoryType(self, category): |
761 def getFileCategoryType(self, category): |
763 """ |
762 """ |
764 Public method to get a user type string for the given category. |
763 Public method to get a user type string for the given category. |
765 |
764 |
766 @param category file type category |
765 @param category file type category |
767 @type str |
766 @type str |
768 @return user type string for the category |
767 @return user type string for the category |
769 @rtype str |
768 @rtype str |
770 """ |
769 """ |
771 return self.__fileCategoryTyeStrings[category] |
770 return self.__fileCategoriesRepository[category].fileCategoryTyeString |
772 |
771 |
773 def getFileCategoryExtension(self, category, reverse=False): |
772 def getFileCategoryExtension(self, category, reverse=False): |
774 """ |
773 """ |
775 Public method to get a list of default file extensions for the given category. |
774 Public method to get a list of default file extensions for the given category. |
776 |
775 |
782 @return list of default file extensions for the category |
781 @return list of default file extensions for the category |
783 @rtype list of str |
782 @rtype list of str |
784 """ |
783 """ |
785 if reverse: |
784 if reverse: |
786 extensions = [] |
785 extensions = [] |
787 for cat, ext in self.__fileCategoryExtensions.items(): |
786 for cat, item in self.__fileCategoriesRepository.items(): |
788 if cat != category: |
787 if cat != category: |
789 extensions += ext |
788 extensions += item.fileCategoryExtensions[:] |
790 return extensions |
789 return extensions |
791 else: |
790 else: |
792 return self.__fileCategoryExtensions[category][:] |
791 return self.__fileCategoriesRepository[category].fileCategoryExtensions[:] |
793 |
792 |
794 def initFileTypes(self): |
793 def initFileTypes(self): |
795 """ |
794 """ |
796 Public method to initialize the filetype associations with default |
795 Public method to initialize the filetype associations with default |
797 values. |
796 values. |
1083 |
1082 |
1084 self.name = os.path.splitext(os.path.basename(fn))[0] |
1083 self.name = os.path.splitext(os.path.basename(fn))[0] |
1085 |
1084 |
1086 # check, if the files of the project still exist in the |
1085 # check, if the files of the project still exist in the |
1087 # project directory |
1086 # project directory |
1088 for fileCategory in self.__knownFileCategories: |
1087 for fileCategory in self.getFileCategories(): |
1089 self.__checkFilesExist(fileCategory) |
1088 self.__checkFilesExist(fileCategory) |
1090 |
1089 |
1091 # get the names of subdirectories the files are stored in |
1090 # get the names of subdirectories the files are stored in |
1092 for fileCategory in [ |
1091 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]: |
1093 c for c in self.__knownFileCategories if c != "OTHERS" |
|
1094 ]: |
|
1095 for fn in self.__pdata[fileCategory]: |
1092 for fn in self.__pdata[fileCategory]: |
1096 dn = os.path.dirname(fn) |
1093 dn = os.path.dirname(fn) |
1097 if dn not in self.subdirs: |
1094 if dn not in self.subdirs: |
1098 self.subdirs.append(dn) |
1095 self.subdirs.append(dn) |
1099 |
1096 |
2150 """Reason: {1}</p>""" |
2147 """Reason: {1}</p>""" |
2151 ).format(oldfn, str(msg)), |
2148 ).format(oldfn, str(msg)), |
2152 ) |
2149 ) |
2153 return False |
2150 return False |
2154 |
2151 |
2155 if any(fn in self.__pdata[category] for category in self.__knownFileCategories): |
2152 if any(fn in self.__pdata[category] for category in self.getFileCategories()): |
2156 self.renameFileInPdata(oldfn, newfn, isSourceFile) |
2153 self.renameFileInPdata(oldfn, newfn, isSourceFile) |
2157 |
2154 |
2158 return True |
2155 return True |
2159 |
2156 |
2160 def renameFileInPdata(self, oldname, newname, isSourceFile=False): |
2157 def renameFileInPdata(self, oldname, newname, isSourceFile=False): |
2187 @return list of files starting with a common prefix (list of strings) |
2184 @return list of files starting with a common prefix (list of strings) |
2188 """ |
2185 """ |
2189 filelist = [] |
2186 filelist = [] |
2190 start = self.getRelativePath(start) |
2187 start = self.getRelativePath(start) |
2191 for fileCategory in [ |
2188 for fileCategory in [ |
2192 c for c in self.__knownFileCategories if c != "TRANSLATIONS" |
2189 c for c in self.getFileCategories() if c != "TRANSLATIONS" |
2193 ]: |
2190 ]: |
2194 for entry in self.__pdata[fileCategory][:]: |
2191 for entry in self.__pdata[fileCategory][:]: |
2195 if entry.startswith(start): |
2192 if entry.startswith(start): |
2196 filelist.append(os.path.join(self.ppath, entry)) |
2193 filelist.append(os.path.join(self.ppath, entry)) |
2197 return filelist |
2194 return filelist |
2202 """ |
2199 """ |
2203 reorganized = False |
2200 reorganized = False |
2204 |
2201 |
2205 # init data store for the reorganization |
2202 # init data store for the reorganization |
2206 newPdata = {} |
2203 newPdata = {} |
2207 for fileCategory in self.__knownFileCategories: |
2204 for fileCategory in self.getFileCategories(): |
2208 newPdata[fileCategory] = [] |
2205 newPdata[fileCategory] = [] |
2209 |
2206 |
2210 # iterate over all files checking for a reassignment |
2207 # iterate over all files checking for a reassignment |
2211 for fileCategory in self.__knownFileCategories: |
2208 for fileCategory in self.getFileCategories(): |
2212 for fn in self.__pdata[fileCategory][:]: |
2209 for fn in self.__pdata[fileCategory][:]: |
2213 filetype = fileCategory |
2210 filetype = fileCategory |
2214 bfn = os.path.basename(fn) |
2211 bfn = os.path.basename(fn) |
2215 for pattern in sorted(self.__pdata["FILETYPES"].keys(), reverse=True): |
2212 for pattern in sorted(self.__pdata["FILETYPES"].keys(), reverse=True): |
2216 if fnmatch.fnmatch(bfn, pattern): |
2213 if fnmatch.fnmatch(bfn, pattern): |
2285 ##"PROTOCOLS", |
2282 ##"PROTOCOLS", |
2286 ##"RESOURCES", |
2283 ##"RESOURCES", |
2287 ##"OTHERS", |
2284 ##"OTHERS", |
2288 ##]: |
2285 ##]: |
2289 for fileCategory in [ |
2286 for fileCategory in [ |
2290 c for c in self.__knownFileCategories if c != "TRANSLATIONS" |
2287 c for c in self.getFileCategories() if c != "TRANSLATIONS" |
2291 ]: |
2288 ]: |
2292 for entry in self.__pdata[fileCategory][:]: |
2289 for entry in self.__pdata[fileCategory][:]: |
2293 if entry.startswith(olddn): |
2290 if entry.startswith(olddn): |
2294 if fileCategory not in typeStrings: |
2291 if fileCategory not in typeStrings: |
2295 typeStrings.append(fileCategory) |
2292 typeStrings.append(fileCategory) |
2346 for entry in self.__pdata["OTHERS"][:]: |
2343 for entry in self.__pdata["OTHERS"][:]: |
2347 if entry.startswith(dn): |
2344 if entry.startswith(dn): |
2348 self.__pdata["OTHERS"].remove(entry) |
2345 self.__pdata["OTHERS"].remove(entry) |
2349 dirty = True |
2346 dirty = True |
2350 dn2 = dn if dn.endswith(os.sep) else dn + os.sep |
2347 dn2 = dn if dn.endswith(os.sep) else dn + os.sep |
2351 for fileCategory in [c for c in self.__knownFileCategories if c != "OTHERS"]: |
2348 for fileCategory in [c for c in self.getFileCategories() if c != "OTHERS"]: |
2352 for entry in self.__pdata[fileCategory][:]: |
2349 for entry in self.__pdata[fileCategory][:]: |
2353 if entry.startswith(dn2): |
2350 if entry.startswith(dn2): |
2354 self.__pdata[fileCategory].remove(entry) |
2351 self.__pdata[fileCategory].remove(entry) |
2355 dirty = True |
2352 dirty = True |
2356 self.__model.removeItem(dn) |
2353 self.__model.removeItem(dn) |
2441 @return flag indicating, if the project contains the file (boolean) |
2438 @return flag indicating, if the project contains the file (boolean) |
2442 """ |
2439 """ |
2443 fn = self.getRelativePath(fn) |
2440 fn = self.getRelativePath(fn) |
2444 return any( |
2441 return any( |
2445 fn in self.__pdata[category] |
2442 fn in self.__pdata[category] |
2446 for category in self.__knownFileCategories |
2443 for category in self.getFileCategories() |
2447 if category != "TRANSLATIONS" |
2444 if category != "TRANSLATIONS" |
2448 ) |
2445 ) |
2449 |
2446 |
2450 def createNewProject(self): |
2447 def createNewProject(self): |
2451 """ |
2448 """ |
3590 @type boolean |
3587 @type boolean |
3591 @return list of file names |
3588 @return list of file names |
3592 @rtype list of str |
3589 @rtype list of str |
3593 @exception ValueError raised when an unsupported file type is given |
3590 @exception ValueError raised when an unsupported file type is given |
3594 """ |
3591 """ |
3595 if fileType not in self.__knownFileCategories: |
3592 if fileType not in self.getFileCategories(): |
3596 raise ValueError("Given file type has incorrect value.") |
3593 raise ValueError("Given file type has incorrect value.") |
3597 |
3594 |
3598 if normalized: |
3595 if normalized: |
3599 return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]] |
3596 return [os.path.join(self.ppath, fn) for fn in self.__pdata[fileType]] |
3600 else: |
3597 else: |
4004 @rtype bool |
4001 @rtype bool |
4005 """ |
4002 """ |
4006 newfn = os.path.abspath(fn) |
4003 newfn = os.path.abspath(fn) |
4007 newfn = self.getRelativePath(newfn) |
4004 newfn = self.getRelativePath(newfn) |
4008 return any( |
4005 return any( |
4009 newfn in self.__pdata[category] |
4006 newfn in self.__pdata[category] for category in self.getFileCategories() |
4010 for category in self.__knownFileCategories |
|
4011 ) |
4007 ) |
4012 |
4008 |
4013 def isProjectFile(self, fn): |
4009 def isProjectFile(self, fn): |
4014 """ |
4010 """ |
4015 Public method used to check, if the passed in filename belongs to the |
4011 Public method used to check, if the passed in filename belongs to the |
4018 @param fn filename to be checked (string) |
4014 @param fn filename to be checked (string) |
4019 @return flag indicating membership (boolean) |
4015 @return flag indicating membership (boolean) |
4020 """ |
4016 """ |
4021 return any( |
4017 return any( |
4022 self.__checkProjectFileGroup(fn, category) |
4018 self.__checkProjectFileGroup(fn, category) |
4023 for category in self.__knownFileCategories |
4019 for category in self.getFileCategories() |
4024 ) |
4020 ) |
4025 |
4021 |
4026 def __checkProjectFileGroup(self, fn, group): |
4022 def __checkProjectFileGroup(self, fn, group): |
4027 """ |
4023 """ |
4028 Private method to check, if a file is in a specific file group of the |
4024 Private method to check, if a file is in a specific file group of the |
5509 if fnmatch.fnmatch(bfn, pattern): |
5505 if fnmatch.fnmatch(bfn, pattern): |
5510 filetype = self.__pdata["FILETYPES"][pattern] |
5506 filetype = self.__pdata["FILETYPES"][pattern] |
5511 break |
5507 break |
5512 |
5508 |
5513 if ( |
5509 if ( |
5514 filetype in self.__knownFileCategories |
5510 filetype in self.getFileCategories() |
5515 and fn not in self.__pdata[filetype] |
5511 and fn not in self.__pdata[filetype] |
5516 and ( |
5512 and ( |
5517 filetype != "TRANSLATIONS" |
5513 filetype != "TRANSLATIONS" |
5518 or ( |
5514 or ( |
5519 filetype == "TRANSLATIONS" |
5515 filetype == "TRANSLATIONS" |