diff --git a/picard/ui/scripteditor.py b/picard/ui/scripteditor.py index d138ccbd9..960fb0e28 100644 --- a/picard/ui/scripteditor.py +++ b/picard/ui/scripteditor.py @@ -26,6 +26,7 @@ import os.path from PyQt5 import ( QtCore, + QtGui, QtWidgets, ) from PyQt5.QtGui import QPalette @@ -45,6 +46,10 @@ from picard.script import ( ScriptParser, get_file_naming_script_presets, ) +from picard.util import ( + icontheme, + webbrowser2, +) from picard.util.settingsoverride import SettingsOverride from picard.ui import PicardDialog @@ -285,33 +290,24 @@ class ScriptEditorPage(PicardDialog): self.loading = True self.ui = Ui_ScriptEditor() self.ui.setupUi(self) - - self.ui.example_filename_sample_files_button.setToolTip(_(self.examples.tooltip_text) % self.examples.max_samples) + self.make_menu() + self.add_tooltips() self.ui.label.setWordWrap(False) self.installEventFilter(self) - self.ui.file_naming_editor_new.clicked.connect(self.new_script) self.ui.file_naming_editor_save.clicked.connect(self.save_script) - self.ui.file_naming_editor_copy.clicked.connect(self.copy_script) self.ui.file_naming_editor_close.clicked.connect(self.close_window) - self.ui.file_naming_editor_delete.clicked.connect(self.delete_script) self.ui.file_naming_editor_reset.clicked.connect(self.reset_script) - self.ui.script_details.clicked.connect(self.view_script_details) self.ui.file_naming_format.setEnabled(True) # Add scripting documentation to parent frame. - doc_widget = ScriptingDocumentationWidget(self) + doc_widget = ScriptingDocumentationWidget(self, include_link=False) self.ui.documentation_frame_layout.addWidget(doc_widget) - self.ui.show_documentation.stateChanged.connect(self.toggle_documentation) self.ui.file_naming_format.textChanged.connect(self.check_formats) - self.ui.file_naming_word_wrap.stateChanged.connect(self.toggle_wordwrap) - self.ui.import_script.clicked.connect(self.import_script) - self.ui.export_script.clicked.connect(self.export_script) - self.ui.example_filename_sample_files_button.clicked.connect(self.update_example_files) self._sampled_example_files = [] self.ui.example_filename_after.itemSelectionChanged.connect(self.match_before_to_after) @@ -328,12 +324,136 @@ class ScriptEditorPage(PicardDialog): self.synchronize_vertical_scrollbars((self.ui.example_filename_before, self.ui.example_filename_after)) - self.wordwrap = QtWidgets.QTextEdit.NoWrap + self.wordwrap = True + self.toggle_wordwrap() # Force update to display + self.sidebar = True + self.toggle_documentation() # Force update to display self.examples_current_row = -1 + self.select_script() - self.load() self.loading = False + def make_menu(self): + """Build the menu bar. + """ + main_menu = QtWidgets.QMenuBar() + base_font = self.ui.file_naming_editor_close.font() + main_menu.setStyleSheet( + "QMenuBar { font-family: %s; font-size: %upt; }" % + (base_font.family(), base_font.pointSize()) + ) + + # File menu settings + file_menu = main_menu.addMenu(_('&File')) + file_menu.setToolTipsVisible(True) + + self.import_action = QtWidgets.QAction(_("&Import a script file"), self) + self.import_action.setIcon(icontheme.lookup('document-open')) + self.import_action.triggered.connect(self.import_script) + file_menu.addAction(self.import_action) + + self.export_action = QtWidgets.QAction(_("&Export a script file"), self) + self.export_action.setIcon(icontheme.lookup('document-save')) + self.export_action.triggered.connect(self.export_script) + file_menu.addAction(self.export_action) + + self.close_action = QtWidgets.QAction(_("E&xit / Close editor"), self) + self.close_action.triggered.connect(self.close_window) + file_menu.addAction(self.close_action) + + # Script menu settings + script_menu = main_menu.addMenu(_('&Script')) + script_menu.setToolTipsVisible(True) + + self.details_action = QtWidgets.QAction(_("&View/Edit Script Metadata"), self) + self.details_action.triggered.connect(self.view_script_details) + script_menu.addAction(self.details_action) + + self.add_action = QtWidgets.QAction(_("Add a &new script"), self) + self.add_action.setIcon(icontheme.lookup('add-item')) + self.add_action.triggered.connect(self.new_script) + script_menu.addAction(self.add_action) + + self.copy_action = QtWidgets.QAction(_("&Copy the current script"), self) + self.copy_action.setIcon(icontheme.lookup('edit-copy')) + self.copy_action.triggered.connect(self.view_script_details) + script_menu.addAction(self.copy_action) + + self.delete_action = QtWidgets.QAction(_("&Delete the current script"), self) + self.delete_action.setIcon(icontheme.lookup('list-remove')) + self.delete_action.triggered.connect(self.delete_script) + script_menu.addAction(self.delete_action) + + self.reset_action = QtWidgets.QAction(_("&Revert the current script"), self) + self.reset_action.setIcon(icontheme.lookup('view-refresh')) + self.reset_action.triggered.connect(self.reset_script) + script_menu.addAction(self.reset_action) + + self.save_action = QtWidgets.QAction(_("&Save the current script"), self) + self.save_action.setIcon(icontheme.lookup('document-save')) + self.save_action.triggered.connect(self.save_script) + script_menu.addAction(self.save_action) + + # Miscellaneous menu settings + tools_menu = main_menu.addMenu(_('&Tools')) + tools_menu.setToolTipsVisible(True) + + self.examples_action = QtWidgets.QAction(_("&Reload random example files"), self) + self.examples_action.setIcon(icontheme.lookup('view-refresh')) + self.examples_action.triggered.connect(self.update_example_files) + tools_menu.addAction(self.examples_action) + + self.wrap_action = QtWidgets.QAction(_("&Toggle word wrap"), self) + self.wrap_action.triggered.connect(self.toggle_wordwrap) + self.wrap_action.setShortcut(QtGui.QKeySequence(_("Ctrl+W"))) + tools_menu.addAction(self.wrap_action) + + # Help menu settings + help_menu = main_menu.addMenu(_('&Help')) + help_menu.setToolTipsVisible(True) + + self.docs_action = QtWidgets.QAction(_("&Show/hide sidebar"), self) + self.docs_action.triggered.connect(self.toggle_documentation) + self.docs_action.setShortcut(QtGui.QKeySequence(_("Ctrl+H"))) + help_menu.addAction(self.docs_action) + + self.docs_browse_action = QtWidgets.QAction(_("&Open in browser"), self) + self.docs_browse_action.setIcon(icontheme.lookup('lookup-musicbrainz')) + self.docs_browse_action.triggered.connect(self.docs_browser) + help_menu.addAction(self.docs_browse_action) + + self.ui.layout_for_menubar.addWidget(main_menu) + + def add_tooltips(self): + """Add tooltips to the widgets. + """ + # Done here to avoid duplication and allow changes/corrections without having to recompile the UI file. + + # Menu items + self.import_action.setToolTip(_("Import a file as a new script")) + self.export_action.setToolTip(_("Export the script to a file")) + self.close_action.setToolTip(_("Close the script editor")) + self.details_action.setToolTip(_("Display the details for the script")) + self.add_action.setToolTip(_("Create a new file naming script")) + self.copy_action.setToolTip(_("Save a copy of the script as a new script")) + self.delete_action.setToolTip(_("Delete the script")) + self.reset_action.setToolTip(_("Revert the script to the last saved value")) + self.save_action.setToolTip(_("Save changes to the script")) + self.examples_action.setToolTip(_(self.examples.tooltip_text) % self.examples.max_samples) + self.wrap_action.setToolTip(_("Word wrap long lines in the editor")) + self.docs_action.setToolTip(_("View the scripting documentation in a sidebar")) + self.docs_browse_action.setToolTip(_("Open the scripting documentation in your browser")) + + # Buttons + self.ui.file_naming_editor_close.setToolTip(self.close_action.toolTip()) + self.ui.file_naming_editor_save.setToolTip(self.save_action.toolTip()) + self.ui.file_naming_editor_reset.setToolTip(self.reset_action.toolTip()) + + def docs_browser(self): + """Open the scriping documentation in a browser. + """ + webbrowser2.open('doc_scripting') + @staticmethod def synchronize_vertical_scrollbars(widgets): """Synchronize position of vertical scrollbars and selections for listed widgets. @@ -425,7 +545,8 @@ class ScriptEditorPage(PicardDialog): def toggle_documentation(self): """Toggle the display of the scripting documentation sidebar. """ - self.ui.documentation_frame.setVisible(self.ui.show_documentation.isChecked()) + self.sidebar = not self.sidebar + self.ui.documentation_frame.setVisible(self.sidebar) def view_script_details(self): """View and edit (if not readonly) the metadata associated with the script. @@ -566,12 +687,20 @@ class ScriptEditorPage(PicardDialog): script_item = self.get_selected_item() readonly = script_item['readonly'] self.ui.script_title.setReadOnly(readonly or selected < 1) + + # Buttons self.ui.file_naming_format.setReadOnly(readonly) self.ui.file_naming_editor_save.setEnabled(save_enabled and not readonly) - self.ui.file_naming_editor_copy.setEnabled(save_enabled) - self.ui.file_naming_editor_delete.setEnabled(script_item['deletable'] and save_enabled) - self.ui.import_script.setEnabled(save_enabled) - self.ui.export_script.setEnabled(save_enabled) + self.ui.file_naming_editor_reset.setEnabled(not readonly) + + # Menu items + self.save_action.setEnabled(save_enabled and not readonly) + self.reset_action.setEnabled(not readonly) + self.add_action.setEnabled(save_enabled) + self.copy_action.setEnabled(save_enabled) + self.delete_action.setEnabled(script_item['deletable'] and save_enabled) + self.import_action.setEnabled(save_enabled) + self.export_action.setEnabled(save_enabled) @staticmethod def synchronize_selected_example_lines(current_row, source, target): @@ -675,7 +804,8 @@ class ScriptEditorPage(PicardDialog): def toggle_wordwrap(self): """Toggles wordwrap in the script editing textbox. """ - if self.ui.file_naming_word_wrap.isChecked(): + self.wordwrap = not self.wordwrap + if self.wordwrap: self.ui.file_naming_format.setLineWrapMode(QtWidgets.QTextEdit.WidgetWidth) else: self.ui.file_naming_format.setLineWrapMode(QtWidgets.QTextEdit.NoWrap) @@ -797,13 +927,6 @@ class ScriptEditorPage(PicardDialog): ) dialog.exec_() - def load(self): - """Loads the file naming script from the selected combo box item. - """ - self.toggle_wordwrap() - self.toggle_documentation() - self.select_script() - def check_formats(self): """Checks for valid file naming script and settings, and updates the examples. """ diff --git a/picard/ui/ui_scripteditor.py b/picard/ui/ui_scripteditor.py index 80771c1ec..cbc1dbb53 100644 --- a/picard/ui/ui_scripteditor.py +++ b/picard/ui/ui_scripteditor.py @@ -12,14 +12,20 @@ class Ui_ScriptEditor(object): ScriptEditor.setObjectName("ScriptEditor") ScriptEditor.setWindowModality(QtCore.Qt.NonModal) ScriptEditor.setEnabled(True) - ScriptEditor.resize(858, 729) + ScriptEditor.resize(902, 729) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(ScriptEditor.sizePolicy().hasHeightForWidth()) ScriptEditor.setSizePolicy(sizePolicy) self.verticalLayout_3 = QtWidgets.QVBoxLayout(ScriptEditor) + self.verticalLayout_3.setContentsMargins(-1, 3, -1, -1) + self.verticalLayout_3.setSpacing(1) self.verticalLayout_3.setObjectName("verticalLayout_3") + self.layout_for_menubar = QtWidgets.QHBoxLayout() + self.layout_for_menubar.setSpacing(6) + self.layout_for_menubar.setObjectName("layout_for_menubar") + self.verticalLayout_3.addLayout(self.layout_for_menubar) self.splitter_between_editor_and_examples = QtWidgets.QSplitter(ScriptEditor) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) @@ -41,6 +47,7 @@ class Ui_ScriptEditor(object): self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) self.frame_4.setObjectName("frame_4") self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame_4) + self.verticalLayout_5.setContentsMargins(0, 3, 0, 0) self.verticalLayout_5.setObjectName("verticalLayout_5") self.horizontalLayout_2 = QtWidgets.QHBoxLayout() self.horizontalLayout_2.setContentsMargins(-1, -1, -1, 0) @@ -127,12 +134,9 @@ class Ui_ScriptEditor(object): self.documentation_frame_layout.setObjectName("documentation_frame_layout") self.verticalLayout_8.addWidget(self.splitter_between_editor_and_documentation) self.verticalLayout_5.addWidget(self.frame) - self.horizontalLayout_5 = QtWidgets.QHBoxLayout() - self.horizontalLayout_5.setContentsMargins(-1, 0, -1, -1) - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.file_naming_word_wrap = QtWidgets.QCheckBox(self.frame_4) - self.file_naming_word_wrap.setObjectName("file_naming_word_wrap") - self.horizontalLayout_5.addWidget(self.file_naming_word_wrap) + self.horizontalLayout_4 = QtWidgets.QHBoxLayout() + self.horizontalLayout_4.setContentsMargins(-1, 0, -1, -1) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") self.renaming_error = QtWidgets.QLabel(self.frame_4) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -142,45 +146,17 @@ class Ui_ScriptEditor(object): self.renaming_error.setText("") self.renaming_error.setAlignment(QtCore.Qt.AlignCenter) self.renaming_error.setObjectName("renaming_error") - self.horizontalLayout_5.addWidget(self.renaming_error) - self.show_documentation = QtWidgets.QCheckBox(self.frame_4) - self.show_documentation.setObjectName("show_documentation") - self.horizontalLayout_5.addWidget(self.show_documentation) - self.verticalLayout_5.addLayout(self.horizontalLayout_5) - self.horizontalLayout_4 = QtWidgets.QHBoxLayout() - self.horizontalLayout_4.setContentsMargins(-1, 0, -1, -1) - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.example_filename_sample_files_button = QtWidgets.QPushButton(self.frame_4) - self.example_filename_sample_files_button.setObjectName("example_filename_sample_files_button") - self.horizontalLayout_4.addWidget(self.example_filename_sample_files_button) - spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_4.addItem(spacerItem) - self.script_details = QtWidgets.QPushButton(self.frame_4) - self.script_details.setStatusTip("") - self.script_details.setObjectName("script_details") - self.horizontalLayout_4.addWidget(self.script_details) - self.import_script = QtWidgets.QPushButton(self.frame_4) - self.import_script.setObjectName("import_script") - self.horizontalLayout_4.addWidget(self.import_script) - self.export_script = QtWidgets.QPushButton(self.frame_4) - self.export_script.setObjectName("export_script") - self.horizontalLayout_4.addWidget(self.export_script) - self.file_naming_editor_new = QtWidgets.QPushButton(self.frame_4) - self.file_naming_editor_new.setObjectName("file_naming_editor_new") - self.horizontalLayout_4.addWidget(self.file_naming_editor_new) - self.file_naming_editor_copy = QtWidgets.QPushButton(self.frame_4) - self.file_naming_editor_copy.setObjectName("file_naming_editor_copy") - self.horizontalLayout_4.addWidget(self.file_naming_editor_copy) - self.file_naming_editor_delete = QtWidgets.QPushButton(self.frame_4) - self.file_naming_editor_delete.setObjectName("file_naming_editor_delete") - self.horizontalLayout_4.addWidget(self.file_naming_editor_delete) + self.horizontalLayout_4.addWidget(self.renaming_error) self.file_naming_editor_reset = QtWidgets.QPushButton(self.frame_4) + self.file_naming_editor_reset.setToolTip("") self.file_naming_editor_reset.setObjectName("file_naming_editor_reset") self.horizontalLayout_4.addWidget(self.file_naming_editor_reset) self.file_naming_editor_save = QtWidgets.QPushButton(self.frame_4) + self.file_naming_editor_save.setToolTip("") self.file_naming_editor_save.setObjectName("file_naming_editor_save") self.horizontalLayout_4.addWidget(self.file_naming_editor_save) self.file_naming_editor_close = QtWidgets.QPushButton(self.frame_4) + self.file_naming_editor_close.setToolTip("") self.file_naming_editor_close.setObjectName("file_naming_editor_close") self.horizontalLayout_4.addWidget(self.file_naming_editor_close) self.verticalLayout_5.addLayout(self.horizontalLayout_4) @@ -244,29 +220,8 @@ class Ui_ScriptEditor(object): self.label.setText(_("Select the file naming script to use:")) self.preset_naming_scripts.setToolTip(_("Select the file naming script to load into the editor")) self.label_2.setText(_("Title:")) - self.file_naming_word_wrap.setToolTip(_("Word wrap long lines in the editor")) - self.file_naming_word_wrap.setText(_("Word wrap script")) - self.show_documentation.setToolTip(_("Open the scripting documentation in a sidebar")) - self.show_documentation.setText(_("Show Documentation")) - self.example_filename_sample_files_button.setToolTip(_("Up to 10 items chosen at random from files selected in the main window")) - self.example_filename_sample_files_button.setText(_("Reload Examples")) - self.script_details.setToolTip(_("Display the details for the script")) - self.script_details.setText(_("Details")) - self.import_script.setToolTip(_("Import a file as a new script")) - self.import_script.setText(_("Import")) - self.export_script.setToolTip(_("Export the script to a file")) - self.export_script.setText(_("Export")) - self.file_naming_editor_new.setToolTip(_("Create a new file naming script")) - self.file_naming_editor_new.setText(_("New")) - self.file_naming_editor_copy.setToolTip(_("Save a copy of the script as a new script")) - self.file_naming_editor_copy.setText(_("Copy")) - self.file_naming_editor_delete.setToolTip(_("Delete the script")) - self.file_naming_editor_delete.setText(_("Delete")) - self.file_naming_editor_reset.setToolTip(_("Reset the script to the last saved value")) - self.file_naming_editor_reset.setText(_("Reset")) - self.file_naming_editor_save.setToolTip(_("Save changes to the script")) + self.file_naming_editor_reset.setText(_("Revert")) self.file_naming_editor_save.setText(_("Save")) - self.file_naming_editor_close.setToolTip(_("Close the script editor")) self.file_naming_editor_close.setText(_("Close")) self.groupBox.setTitle(_("Files will be named like this")) self.example_filename_before_label.setText(_("Before")) diff --git a/picard/ui/widgets/scriptdocumentation.py b/picard/ui/widgets/scriptdocumentation.py index 70af7a6f2..6d94ff5bf 100644 --- a/picard/ui/widgets/scriptdocumentation.py +++ b/picard/ui/widgets/scriptdocumentation.py @@ -59,11 +59,12 @@ code { class ScriptingDocumentationWidget(QtWidgets.QWidget): """Custom widget to display the scripting documentation. """ - def __init__(self, parent, *args, **kwargs): + def __init__(self, parent, include_link=True, *args, **kwargs): """Custom widget to display the scripting documentation. Args: - parent (QWidget): Parent screen to check layoutDirection(). + parent (QWidget): Parent screen to check layoutDirection() + include_link (bool): Indicates whether the web link should be included """ super().__init__(*args, **kwargs) @@ -103,7 +104,7 @@ class ScriptingDocumentationWidget(QtWidgets.QWidget): if text_direction == 'rtl': html = html.replace('', '‎') - link = '' + N_('Open Scripting Documentation in your browser') + '' + link = '' + _('Open Scripting Documentation in your browser') + '' self.verticalLayout = QtWidgets.QVBoxLayout(self) self.verticalLayout.setContentsMargins(0, 0, 0, 0) @@ -123,13 +124,14 @@ class ScriptingDocumentationWidget(QtWidgets.QWidget): sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.scripting_doc_link.sizePolicy().hasHeightForWidth()) - self.scripting_doc_link.setSizePolicy(sizePolicy) - self.scripting_doc_link.setMinimumSize(QtCore.QSize(0, 20)) - self.scripting_doc_link.setAlignment(QtCore.Qt.AlignCenter) - self.scripting_doc_link.setWordWrap(True) - self.scripting_doc_link.setOpenExternalLinks(True) - self.scripting_doc_link.setObjectName("docs_scripting_doc_link") - self.scripting_doc_link.setText(link) - self.scripting_doc_link.show() - self.horizontalLayout.addWidget(self.scripting_doc_link) + if include_link: + self.scripting_doc_link.setSizePolicy(sizePolicy) + self.scripting_doc_link.setMinimumSize(QtCore.QSize(0, 20)) + self.scripting_doc_link.setAlignment(QtCore.Qt.AlignCenter) + self.scripting_doc_link.setWordWrap(True) + self.scripting_doc_link.setOpenExternalLinks(True) + self.scripting_doc_link.setObjectName("docs_scripting_doc_link") + self.scripting_doc_link.setText(link) + self.scripting_doc_link.show() + self.horizontalLayout.addWidget(self.scripting_doc_link) self.verticalLayout.addLayout(self.horizontalLayout) diff --git a/ui/scripteditor.ui b/ui/scripteditor.ui index 9ddaf6077..fcef2244a 100644 --- a/ui/scripteditor.ui +++ b/ui/scripteditor.ui @@ -12,7 +12,7 @@ 0 0 - 858 + 902 729 @@ -23,6 +23,19 @@ + + 1 + + + 3 + + + + + 6 + + + @@ -63,6 +76,18 @@ QFrame::Raised + + 0 + + + 3 + + + 0 + + + 0 + @@ -271,20 +296,10 @@ - + 0 - - - - Word wrap long lines in the editor - - - Word wrap script - - - @@ -302,112 +317,19 @@ - + - Open the scripting documentation in a sidebar - - - Show Documentation - - - - - - - - - 0 - - - - - Up to 10 items chosen at random from files selected in the main window - - - Reload Examples - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Display the details for the script - - - Details - - - - - - - Import a file as a new script - - - Import - - - - - - - Export the script to a file - - - Export - - - - - - - Create a new file naming script - - - New - - - - - - - Save a copy of the script as a new script - - - Copy - - - - - - - Delete the script - - - Delete + Revert - Save changes to the script + Save @@ -417,7 +339,7 @@ - Close the script editor + Close