From 3017b3810c06c7abe91e18bf65499bb092663a56 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 11 Oct 2024 13:38:34 -0500 Subject: [PATCH] Adding new validation logic to prevent deletion of custom profile which is currently used or the default project (in settings). Also validating custom profile has a unique description. Adding icons to Profile context menu. Added a new "Set as Default Profile" context menu option. Updated translations. --- src/language/OpenShot/OpenShot.pot | 88 ++++++++++++++------------ src/windows/main_window.py | 14 +++- src/windows/profile.py | 4 +- src/windows/profile_edit.py | 19 +++++- src/windows/views/profiles_treeview.py | 21 ++++-- 5 files changed, 92 insertions(+), 54 deletions(-) diff --git a/src/language/OpenShot/OpenShot.pot b/src/language/OpenShot/OpenShot.pot index 1e184f11f..838372c36 100644 --- a/src/language/OpenShot/OpenShot.pot +++ b/src/language/OpenShot/OpenShot.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: OpenShot Video Editor (version: 3.2.1-dev)\n" "Report-Msgid-Bugs-To: Jonathan Thomas \n" -"POT-Creation-Date: 2024-10-09 16:12:14.191928\n" +"POT-Creation-Date: 2024-10-11 13:36:18.474319\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Jonathan Thomas \n" "Language-Team: https://translations.launchpad.net/+groups/launchpad-translators\n" @@ -50,7 +50,7 @@ msgstr "" #: /home/jonathan/apps/openshot-qt/src/windows/export.py:91 #: /home/jonathan/apps/openshot-qt/src/windows/export.py:889 #: /home/jonathan/apps/openshot-qt/src/windows/export.py:900 -#: /home/jonathan/apps/openshot-qt/src/windows/export.py:1243 Settings for +#: /home/jonathan/apps/openshot-qt/src/windows/export.py:1250 Settings for #: actionExportVideo #: /home/jonathan/apps/openshot-qt/src/windows/ui/export.ui:20 msgid "Export Video" @@ -78,7 +78,7 @@ msgstr "" #: /home/jonathan/apps/openshot-qt/src/windows/export.py:164 #: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:660 #: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:758 -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2606 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2597 #: /home/jonathan/apps/openshot-qt/src/classes/exporters/edl.py:56 #: /home/jonathan/apps/openshot-qt/src/classes/exporters/final_cut_pro.py:90 msgid "Untitled Project" @@ -157,8 +157,8 @@ msgstr "" #: /home/jonathan/apps/openshot-qt/src/windows/export.py:489 #: /home/jonathan/apps/openshot-qt/src/windows/file_properties.py:161 -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:54 -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:68 libopenshot +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:80 +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:94 libopenshot #: (Clip Properties) msgid "No" msgstr "" @@ -228,7 +228,7 @@ msgid "" "%s" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/export.py:1244 +#: /home/jonathan/apps/openshot-qt/src/windows/export.py:1251 msgid "Are you sure you want to cancel the export?" msgstr "" @@ -288,7 +288,7 @@ msgstr "" #: /home/jonathan/apps/openshot-qt/src/windows/views/properties_tableview.py:700 #: /home/jonathan/apps/openshot-qt/src/windows/views/timeline.py:149 -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2185 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2176 #: /home/jonathan/apps/openshot-qt/src/windows/models/properties_model.py:784 #: /home/jonathan/apps/openshot-qt/src/windows/models/properties_model.py:873 #: /home/jonathan/apps/openshot-qt/src/windows/add_to_timeline.py:480 @@ -972,23 +972,27 @@ msgstr "" #: /home/jonathan/apps/openshot-qt/src/windows/views/files_listview.py:98 #: /home/jonathan/apps/openshot-qt/src/windows/views/files_treeview.py:101 -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:50 +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:74 #: /home/jonathan/apps/openshot-qt/src/windows/ui/profile-edit.ui:14 msgid "Create Profile" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/views/profiles_treeview.py:90 -msgid "Edit" +#: /home/jonathan/apps/openshot-qt/src/windows/views/profiles_treeview.py:99 +msgid "Set as Default Profile" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/views/profiles_treeview.py:94 +#: /home/jonathan/apps/openshot-qt/src/windows/views/profiles_treeview.py:105 #: Settings for actionDuplicate #: /home/jonathan/apps/openshot-qt/src/windows/ui/main-window.ui:1554 #: /home/jonathan/apps/openshot-qt/src/windows/ui/main-window.ui:1557 msgid "Duplicate" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/views/profiles_treeview.py:100 +#: /home/jonathan/apps/openshot-qt/src/windows/views/profiles_treeview.py:113 +msgid "Edit" +msgstr "" + +#: /home/jonathan/apps/openshot-qt/src/windows/views/profiles_treeview.py:118 msgid "Delete" msgstr "" @@ -1267,78 +1271,78 @@ msgstr "" msgid "Enable Razor" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:1768 -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:156 -msgid "Error" +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:1773 +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:181 +msgid "Profile Error" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:1768 -msgid "You can not delete the current profile." +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:1774 +msgid "You can not delete the current or default profile." msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2116 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2107 msgid "Error Removing Track" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2116 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2107 msgid "You must keep at least 1 track" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2187 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2178 #: /home/jonathan/apps/openshot-qt/src/windows/ui/main-window.ui:1440 #: /home/jonathan/apps/openshot-qt/src/windows/ui/main-window.ui:1443 msgid "Rename Track" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2187 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2178 msgid "Track Name:" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2365 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2356 msgid "Docks" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2559 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2550 msgid "Enter caption text..." msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2777 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2768 msgid "Recent Projects" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2786 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2777 msgid "No Recent Projects" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2842 -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2858 -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2876 -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2886 -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:3514 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2833 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2849 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2867 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2877 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:3505 #: /home/jonathan/apps/openshot-qt/src/windows/ui/main-window.ui:432 msgid "Filter" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2901 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2892 msgid "Caption Toolbar" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2973 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2964 #: /home/jonathan/apps/openshot-qt/src/windows/ui/main-window.ui:1452 #: /home/jonathan/apps/openshot-qt/src/windows/ui/main-window.ui:1455 msgid "Update Available" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2974 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:2965 #, python-format msgid "Update Available: %s" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:3471 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:3462 msgid "Error starting local HTTP server" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:3472 +#: /home/jonathan/apps/openshot-qt/src/windows/main_window.py:3463 msgid "Failed multiple attempts to start server:" msgstr "" @@ -1559,9 +1563,9 @@ msgid "Unknown" msgstr "" #: /home/jonathan/apps/openshot-qt/src/windows/file_properties.py:160 -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:53 -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:68 -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:133 libopenshot +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:79 +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:94 +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:159 libopenshot #: (Clip Properties) msgid "Yes" msgstr "" @@ -1722,12 +1726,12 @@ msgstr "" msgid "OpenShot on GitHub" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:48 -msgid "Duplicate Profile" +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:76 +msgid "Edit Profile" msgstr "" -#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:156 -msgid "Please enter a description for this profile." +#: /home/jonathan/apps/openshot-qt/src/windows/profile_edit.py:182 +msgid "Please enter a unique description for this profile." msgstr "" #: /home/jonathan/apps/openshot-qt/src/classes/exporters/edl.py:59 diff --git a/src/windows/main_window.py b/src/windows/main_window.py index cf3d1e955..dcc8a4fee 100644 --- a/src/windows/main_window.py +++ b/src/windows/main_window.py @@ -1753,6 +1753,13 @@ def initShortcuts(self): # Log shortcut initialization completion log.debug("Shortcuts initialized or updated.") + def actionProfileDefault_trigger(self, profile=None): + # Set default profile in settings + s = get_app().get_settings() + if profile: + s.set("default-profile", profile.info.description) + log.info(f"Setting default profile to '{profile.info.description}'") + def actionProfileEdit_trigger(self, profile=None, duplicate=False, delete=False, parent=None): # Show profile edit dialog from windows.profile_edit import EditProfileDialog @@ -1763,9 +1770,12 @@ def actionProfileEdit_trigger(self, profile=None, duplicate=False, delete=False, if profile and delete and parent: # Delete profile (no dialog) + error_title = _("Profile Error") + error_message = _("You can not delete the current or default profile.") if os.path.exists(profile.path): - if profile.info.description == get_app().project.get(['profile']): - QMessageBox.warning(parent, _("Error"), _("You can not delete the current profile.")) + if (profile.info.description.strip() == get_app().project.get(['profile']).strip() or + profile.info.description.strip() == get_app().get_settings().get('default-profile').strip()): + QMessageBox.warning(parent, error_title, error_message) return log.info(f"Removing custom profile: {profile.path}") diff --git a/src/windows/profile.py b/src/windows/profile.py index 6d9d22e12..8a0fb587f 100644 --- a/src/windows/profile.py +++ b/src/windows/profile.py @@ -108,9 +108,7 @@ def __init__(self, initial_profile_desc=None): self.profile_list.append(profile) except RuntimeError as e: - # This exception occurs when there's a problem parsing - # the Profile file - display a message and continue - log.error("Failed to parse file '%s' as a profile: %s" % (profile_path, e)) + log.warning("Failed to parse file '%s' as a profile: %s" % (profile_path, e)) # Create treeview self.profileListView = ProfilesTreeView(self, self.profile_list) diff --git a/src/windows/profile_edit.py b/src/windows/profile_edit.py index b0f76af20..b98636a7f 100644 --- a/src/windows/profile_edit.py +++ b/src/windows/profile_edit.py @@ -178,10 +178,27 @@ def accept(self): _ = get_app()._tr # Prevent saving with no description + error_title = _("Profile Error") + error_message = _("Please enter a unique description for this profile.") if not self.profile.info.description.strip(): - QMessageBox.warning(self, _("Error"), _("Please enter a description for this profile.")) + QMessageBox.warning(self, error_title, error_message) return + # Verify description is unique + for profile_folder in [info.USER_PROFILES_PATH, info.PROFILES_PATH]: + for file in reversed(sorted(os.listdir(profile_folder))): + profile_verify_path = os.path.join(profile_folder, file) + if os.path.isdir(profile_verify_path): + continue + try: + # Load Profile + p = openshot.Profile(profile_verify_path) + if p.info.description.strip() == self.profile.info.description.strip(): + QMessageBox.warning(self, error_title, error_message) + return + except RuntimeError as e: + log.warning("Failed to parse file '%s' as a profile: %s" % (profile_verify_path, e)) + # Save the profile data as a text file in the user profiles folder profile_path = self.lblFilePathValue.text() log.info(f"Saving custom profile: {profile_path}") diff --git a/src/windows/views/profiles_treeview.py b/src/windows/views/profiles_treeview.py index 5922b021b..7c981726e 100644 --- a/src/windows/views/profiles_treeview.py +++ b/src/windows/views/profiles_treeview.py @@ -26,6 +26,7 @@ """ from PyQt5.QtCore import Qt, QItemSelectionModel, QRegExp, pyqtSignal, QTimer +from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QListView, QTreeView, QAbstractItemView, QSizePolicy, QAction from classes.app import get_app @@ -95,19 +96,27 @@ def contextMenuEvent(self, event): menu = StyledContextMenu(parent=self) - # Determine if the profile is user-created or not - if hasattr(profile, 'user_created') and profile.user_created: - edit_action = QAction(_("Edit"), self) - edit_action.triggered.connect(lambda: get_app().window.actionProfileEdit_trigger(profile, duplicate=False, parent=self)) - menu.addAction(edit_action) + default_action = QAction(_("Set as Default Profile"), self) + default_action.setIcon(QIcon(":/icons/Humanity/actions/16/bookmark-new.svg")) + default_action.triggered.connect(lambda: get_app().window.actionProfileDefault_trigger(profile)) + menu.addAction(default_action) + menu.addSeparator() duplicate_action = QAction(_("Duplicate"), self) + duplicate_action.setIcon(QIcon(":/icons/Humanity/actions/16/edit-copy.svg")) duplicate_action.triggered.connect(lambda: get_app().window.actionProfileEdit_trigger(profile, duplicate=True, parent=self)) menu.addAction(duplicate_action) - menu.addSeparator() + # Determine if the profile is user-created or not if hasattr(profile, 'user_created') and profile.user_created: + menu.addSeparator() + edit_action = QAction(_("Edit"), self) + edit_action.setIcon(QIcon(":/icons/Humanity/actions/16/gtk-edit.svg")) + edit_action.triggered.connect(lambda: get_app().window.actionProfileEdit_trigger(profile, duplicate=False, parent=self)) + menu.addAction(edit_action) + delete_action = QAction(_("Delete"), self) + delete_action.setIcon(QIcon(":/icons/Humanity/actions/16/edit-delete.svg")) delete_action.triggered.connect(lambda: get_app().window.actionProfileEdit_trigger(profile, delete=True, parent=self)) menu.addAction(delete_action)