diff --git a/resources/images/sort.png b/resources/images/sort.png new file mode 100644 index 0000000000..26f911b7a9 Binary files /dev/null and b/resources/images/sort.png differ diff --git a/src/calibre/ebooks/oeb/polish/css.py b/src/calibre/ebooks/oeb/polish/css.py index 6911709b77..77c1735119 100644 --- a/src/calibre/ebooks/oeb/polish/css.py +++ b/src/calibre/ebooks/oeb/polish/css.py @@ -16,6 +16,7 @@ from calibre import force_unicode from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS from calibre.ebooks.oeb.normalize_css import normalize_filter_css, normalizers from calibre.ebooks.oeb.polish.pretty import pretty_script_or_style +from calibre.utils.icu import numeric_sort_key from css_selectors import Select, SelectorError @@ -324,3 +325,37 @@ def remove_property_value(prop, predicate): x = x.replace(v.cssText, '').strip() prop.propertyValue.cssText = x return bool(removed_vals) + + +RULE_PRIORITIES = {t:i for i, t in enumerate((CSSRule.COMMENT, CSSRule.CHARSET_RULE, CSSRule.IMPORT_RULE, CSSRule.NAMESPACE_RULE))} + +def sort_sheet(container, sheet_or_text): + ''' Sort the rules in a stylesheet. Note that in the general case this can + change the effective styles, but for most common sheets, it should be safe. + ''' + sheet = container.parse_css(sheet_or_text) if isinstance(sheet_or_text, unicode) else sheet_or_text + + def text_sort_key(x): + return numeric_sort_key(unicode(x or '')) + + def selector_sort_key(x): + return (x.specificity, text_sort_key(x.selectorText)) + + def rule_sort_key(rule): + primary = RULE_PRIORITIES.get(rule.type, len(RULE_PRIORITIES)) + secondary = text_sort_key(getattr(rule, 'atkeyword', '') or '') + tertiary = None + if rule.type == CSSRule.STYLE_RULE: + primary += 1 + selectors = sorted(rule.selectorList, key=selector_sort_key) + tertiary = selector_sort_key(selectors[0]) + rule.selectorText = ', '.join(s.selectorText for s in selectors) + elif rule.type == CSSRule.FONT_FACE_RULE: + try: + tertiary = text_sort_key(rule.style.getPropertyValue('font-family')) + except Exception: + pass + + return primary, secondary, tertiary + sheet.cssRules.sort(key=rule_sort_key) + return sheet diff --git a/src/calibre/gui2/tweak_book/__init__.py b/src/calibre/gui2/tweak_book/__init__.py index f29fb25a72..679ff1db0e 100644 --- a/src/calibre/gui2/tweak_book/__init__.py +++ b/src/calibre/gui2/tweak_book/__init__.py @@ -58,7 +58,7 @@ d['global_tools_toolbar'] = [ ] d['global_plugins_toolbar'] = [] d['editor_common_toolbar'] = [('editor-' + x) if x else None for x in ('undo', 'redo', None, 'cut', 'copy', 'paste', 'smart-comment')] -d['editor_css_toolbar'] = ['pretty-current', 'insert-image'] +d['editor_css_toolbar'] = ['pretty-current', 'sort-css', 'insert-image'] d['editor_xml_toolbar'] = ['pretty-current', 'insert-tag'] d['editor_html_toolbar'] = ['fix-html-current', 'pretty-current', 'insert-image', 'insert-hyperlink', 'insert-tag', 'change-paragraph'] d['editor_format_toolbar'] = [('format-text-' + x) if x else x for x in ( diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 83f70f7698..e101858247 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -17,7 +17,7 @@ from PyQt5.Qt import ( QColorDialog, QTimer, pyqtSignal) from calibre import prepare_string_for_xml -from calibre.gui2.tweak_book import tprefs, TOP +from calibre.gui2.tweak_book import tprefs, TOP, current_container from calibre.gui2.tweak_book.completion.popup import CompletionPopup from calibre.gui2.tweak_book.editor import ( SYNTAX_PROPERTY, SPELL_PROPERTY, SPELL_LOCALE_PROPERTY, store_locale, LINK_PROPERTY) @@ -332,6 +332,14 @@ class TextEdit(PlainTextEdit): from calibre.gui2.tweak_book.editor.comments import smart_comment smart_comment(self, self.syntax) + def sort_css(self): + from calibre.gui2.dialogs.confirm_delete import confirm + if confirm(_('Sorting CSS rules can in rare cases change the effective styles applied to the book.' + ' Are you sure you want to proceed?'), 'edit-book-confirm-sort-css', parent=self, config_set=tprefs): + from calibre.ebooks.oeb.polish.css import sort_sheet + text = sort_sheet(current_container(), self.toPlainText()).cssText + self.setPlainText(text) + def find(self, pat, wrap=False, marked=False, complete=False, save_match=None): if marked: return self.find_in_marked(pat, wrap=wrap, save_match=save_match) diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 72a69dceea..5829057084 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -80,6 +80,8 @@ def register_text_editor_actions(_reg, palette): ac = reg('format-justify-fill.png', _('&Justify'), ('format_text', 'justify_justify'), 'format-text-justify-fill', (), _('Justify')) ac.setToolTip(_('