From 66e1fcc6827a89205bb44ff4b9b29c9c0ef91fab Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 6 Mar 2011 10:21:17 +0000 Subject: [PATCH 01/10] Fix #9293 - Category rename is not case sensitive, name can clash with itself --- src/calibre/gui2/tag_view.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 586715afd0..11b696d861 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -1340,7 +1340,8 @@ class TagsModel(QAbstractItemModel): # {{{ for c in sorted(user_cats.keys(), key=sort_key): if icu_lower(c).startswith(ckey_lower): if len(c) == len(ckey): - if nkey_lower in user_cat_keys_lower: + if strcmp(ckey, nkey) != 0 and \ + nkey_lower in user_cat_keys_lower: error_dialog(self.tags_view, _('Rename user category'), _('The name %s is already used')%nkey, show=True) return False @@ -1348,7 +1349,8 @@ class TagsModel(QAbstractItemModel): # {{{ del user_cats[ckey] elif c[len(ckey)] == '.': rest = c[len(ckey):] - if icu_lower(nkey + rest) in user_cat_keys_lower: + if strcmp(ckey, nkey) != 0 and \ + icu_lower(nkey + rest) in user_cat_keys_lower: error_dialog(self.tags_view, _('Rename user category'), _('The name %s is already used')%(nkey+rest), show=True) return False From 6fc235d5f9e577bad944ebde1a97abe9a447302e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 Mar 2011 08:09:32 -0700 Subject: [PATCH 02/10] ... --- resources/images/news/kompiutierra.png | Bin 0 -> 654 bytes resources/images/news/rbc_ru.png | Bin 0 -> 371 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/images/news/kompiutierra.png create mode 100644 resources/images/news/rbc_ru.png diff --git a/resources/images/news/kompiutierra.png b/resources/images/news/kompiutierra.png new file mode 100644 index 0000000000000000000000000000000000000000..272e3d905fb98bf2deef154486357ee490de0d22 GIT binary patch literal 654 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#O#Ex;TbdoL)NVymv^T%u)OFXR}nJbX8Wg zO2?{xYKwY4f5Db+!K_H#D2pcVzx;9C1%em*&z{k8DG|?5;K*fa?@+iUA~PAy(D^Lpd$N$+mX6MC@4@zA%zJKJ3L-Ok$^l&rTu z`ST-=)d%cYmaAO)FIb0vi~<=gKj^J|Ixwwki`CZs6x*S>GQ3vzBXhp*%@#GRrbeTcvepKP!CH z*>s*+_-dB=4u?>)If z?P`?flx>r%6s9qWO=MfS_iR Date: Sun, 6 Mar 2011 08:18:45 -0700 Subject: [PATCH 03/10] Search and replace preferences: Prevent very long strings from causing the wizard button to get pushed off the screen --- src/calibre/gui2/convert/xexp_edit.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/gui2/convert/xexp_edit.ui b/src/calibre/gui2/convert/xexp_edit.ui index 18b7c39b52..68c0c8c98e 100644 --- a/src/calibre/gui2/convert/xexp_edit.ui +++ b/src/calibre/gui2/convert/xexp_edit.ui @@ -43,6 +43,9 @@ 0 + + QComboBox::AdjustToMinimumContentsLengthWithIcon + 30 From a94fe3adad7bfae3d94cdb9e9d11df85c0933756 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 Mar 2011 08:31:58 -0700 Subject: [PATCH 04/10] FB2 Input: Support for tables. Fixes #9302 (fb2 --> epub, mobi Missing tables) --- resources/templates/fb2.xsl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/templates/fb2.xsl b/resources/templates/fb2.xsl index 77c03cdc74..703d082467 100644 --- a/resources/templates/fb2.xsl +++ b/resources/templates/fb2.xsl @@ -293,6 +293,23 @@

Annotation

+ + + + +
+
+ + + + + + + + + + +
From d68aaddbb4d97ca829c29acc8117314fe78363d3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 Mar 2011 08:39:53 -0700 Subject: [PATCH 05/10] ... --- resources/templates/fb2.xsl | 576 ++++++++++++++++++------------------ 1 file changed, 288 insertions(+), 288 deletions(-) diff --git a/resources/templates/fb2.xsl b/resources/templates/fb2.xsl index 703d082467..9d0abc7d45 100644 --- a/resources/templates/fb2.xsl +++ b/resources/templates/fb2.xsl @@ -19,21 +19,21 @@ ######################################################################### --> - - - - - - - - - - <xsl:value-of select="fb:description/fb:title-info/fb:book-title"/> - - + - - - -
- -
-
-
- -
    - -
-
+ + + +
+ +
+
+
+ +
    + +
+
- - - -
-
- -

- -

-
- - -
- - -
- - - - - + + + +
+
+ +

+ +

+
+ + +
+ + +
+ + + + + - -
-
- - -
  • - - - , # - - - -
      - - - -
    -
    - - - - - - - - - -
  • - - -
    -
    -
  • - - -
    - - - - - - -
    -
    + +
    + + + +
  • + + + , # + + + +
      + + + +
    +
    + + + + + + + + + +
  • + + +
    +
    +
  • + + +
    + + + + + + +
    +
    - + @@ -164,15 +164,15 @@ - - - - - + + + + + - - - + + + @@ -181,79 +181,79 @@ TOC_ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - + - -
    -
    - - - - - - - -
    - -
    -
    - - + +
    +
    + + + + + + + +
    + +
    +
    + + paragraph - - - - + + + + - - - - - - - - - - - - - - - -
    -
    +
    + + + + + + + + + + + + + + +
    +
    @@ -261,140 +261,140 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    Annotation

    - -
    - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Annotation

    + +
    + +
    -
    - + + - - + + - + - - - -
    - - - - - - -
    -
    - - -
    - -
    -
    - - -
    - - - - - - -
    -
    - - -
    -
    -
    - - - - -     -
    -
    - -     -
    -
    -
    -
    - - -
    - - - - - - -
    -
    +
    + + +
    + + + + + + +
    +
    + + +
    + +
    +
    + + +
    + + + + + + +
    +
    + + +
    +
    +
    + + + + +     +
    +
    + +     +
    +
    +
    +
    + + +
    + + + + + + +
    +
    - - - -
    -
    - - - - - - - -
    -
    - - -
    - - - - - - - - - - -
    -
    + + + +
    +
    + + + + + + + +
    +
    + + +
    + + + + + + + + + + +
    +
    From bcdd6d474ae2c64735bbda2ae643e1137f411ecf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 Mar 2011 08:42:09 -0700 Subject: [PATCH 06/10] ... --- resources/templates/fb2.xsl | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/templates/fb2.xsl b/resources/templates/fb2.xsl index 9d0abc7d45..273edd71ae 100644 --- a/resources/templates/fb2.xsl +++ b/resources/templates/fb2.xsl @@ -4,6 +4,7 @@ # # # # # copyright 2002 Paul Henry Tremblay # +# Copyright 2011 Kovid Goyal # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # From 43fcdf8ca958e699cd91e1082b78a6a293326665 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 Mar 2011 09:24:55 -0700 Subject: [PATCH 07/10] Replace leading period in filenames with an underscore --- src/calibre/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 221f5911c6..716e3913fb 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -85,6 +85,8 @@ def sanitize_file_name(name, substitute='_', as_unicode=False): # Windows doesn't like path components that end with a period if one.endswith('.'): one = one[:-1]+'_' + if one.startswith('.'): + one = '_' + one[1:] return one From fe1aa601fe3db699d33df52d4b043230127ee663 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 6 Mar 2011 18:13:47 +0000 Subject: [PATCH 08/10] Fix exception in bulk edit when an enumeration value is missing from the valid set --- src/calibre/gui2/custom_column_widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index fa7ba3c56d..8641f9e712 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -795,6 +795,7 @@ class BulkEnumeration(BulkBase, Enumeration): return value def setup_ui(self, parent): + self.parent = parent self.make_widgets(parent, QComboBox) vals = self.col_metadata['display']['enum_values'] self.main_widget.blockSignals(True) From 56a89ca29f90552be35ac4142106b71ee73859bb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 Mar 2011 11:42:22 -0700 Subject: [PATCH 09/10] Fix #7250 ("Save to disk" cannot save non-ASCII file name correctly if not convert to ASCII) --- src/calibre/__init__.py | 28 +++++++++++++++++++- src/calibre/library/save_to_disk.py | 14 ++++------ src/calibre/startup.py | 41 ----------------------------- 3 files changed, 32 insertions(+), 51 deletions(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 716e3913fb..5ba1aa42de 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -61,8 +61,9 @@ def osx_version(): if m: return int(m.group(1)), int(m.group(2)), int(m.group(3)) - _filename_sanitize = re.compile(r'[\xae\0\\|\?\*<":>\+/]') +_filename_sanitize_unicode = frozenset([u'\\', u'|', u'?', u'*', u'<', + u'"', u':', u'>', u'+', u'/'] + list(map(unichr, xrange(32)))) def sanitize_file_name(name, substitute='_', as_unicode=False): ''' @@ -85,6 +86,31 @@ def sanitize_file_name(name, substitute='_', as_unicode=False): # Windows doesn't like path components that end with a period if one.endswith('.'): one = one[:-1]+'_' + # Names starting with a period are hidden on Unix + if one.startswith('.'): + one = '_' + one[1:] + return one + +def sanitize_file_name_unicode(name, substitute='_'): + ''' + Sanitize the filename `name`. All invalid characters are replaced by `substitute`. + The set of invalid characters is the union of the invalid characters in Windows, + OS X and Linux. Also removes leading and trailing whitespace. + **WARNING:** This function also replaces path separators, so only pass file names + and not full paths to it. + ''' + if not isinstance(name, unicode): + return sanitize_file_name(name, substitute=substitute, as_unicode=True) + chars = [substitute if c in _filename_sanitize_unicode else c for c in + name] + one = u''.join(chars) + one = re.sub(r'\s', ' ', one).strip() + one = re.sub(r'^\.+$', '_', one) + one = one.replace('..', substitute) + # Windows doesn't like path components that end with a period or space + if one and one[-1] in ('.', ' '): + one = one[:-1]+'_' + # Names starting with a period are hidden on Unix if one.startswith('.'): one = '_' + one[1:] return one diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index de586048b7..96c42e6e0e 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -12,13 +12,13 @@ from calibre.constants import DEBUG from calibre.utils.config import Config, StringConfig, tweaks from calibre.utils.formatter import TemplateFormatter from calibre.utils.filenames import shorten_components_to, supports_long_names, \ - ascii_filename, sanitize_file_name + ascii_filename from calibre.ebooks.metadata.opf2 import metadata_to_opf from calibre.ebooks.metadata.meta import set_metadata -from calibre.constants import preferred_encoding, filesystem_encoding +from calibre.constants import preferred_encoding from calibre.ebooks.metadata import fmt_sidx from calibre.ebooks.metadata import title_sort -from calibre import strftime, prints +from calibre import strftime, prints, sanitize_file_name_unicode plugboard_any_device_value = 'any device' plugboard_any_format_value = 'any format' @@ -197,12 +197,10 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, format_args[key] = '' components = SafeFormat().safe_format(template, format_args, 'G_C-EXCEPTION!', mi) - components = [x.strip() for x in components.split('/') if x.strip()] + components = [x.strip() for x in components.split('/')] components = [sanitize_func(x) for x in components if x] if not components: components = [str(id)] - components = [x.encode(filesystem_encoding, 'replace') if isinstance(x, - unicode) else x for x in components] if to_lowercase: components = [x.lower() for x in components] if replace_whitespace: @@ -247,7 +245,7 @@ def do_save_book_to_disk(id_, mi, cover, plugboards, return True, id_, mi.title components = get_components(opts.template, mi, id_, opts.timefmt, length, - ascii_filename if opts.asciiize else sanitize_file_name, + ascii_filename if opts.asciiize else sanitize_file_name_unicode, to_lowercase=opts.to_lowercase, replace_whitespace=opts.replace_whitespace) base_path = os.path.join(root, *components) @@ -329,8 +327,6 @@ def do_save_book_to_disk(id_, mi, cover, plugboards, def _sanitize_args(root, opts): if opts is None: opts = config().parse() - if isinstance(root, unicode): - root = root.encode(filesystem_encoding) root = os.path.abspath(root) opts.template = preprocess_template(opts.template) diff --git a/src/calibre/startup.py b/src/calibre/startup.py index 41b20f3946..c883c43e8a 100644 --- a/src/calibre/startup.py +++ b/src/calibre/startup.py @@ -72,47 +72,6 @@ if not _run_once: pass ################################################################################ - # Improve builtin path functions to handle unicode sensibly - - _abspath = os.path.abspath - def my_abspath(path, encoding=sys.getfilesystemencoding()): - ''' - Work around for buggy os.path.abspath. This function accepts either byte strings, - in which it calls os.path.abspath, or unicode string, in which case it first converts - to byte strings using `encoding`, calls abspath and then decodes back to unicode. - ''' - to_unicode = False - if encoding is None: - encoding = preferred_encoding - if isinstance(path, unicode): - path = path.encode(encoding) - to_unicode = True - res = _abspath(path) - if to_unicode: - res = res.decode(encoding) - return res - - os.path.abspath = my_abspath - - _join = os.path.join - def my_join(a, *p): - encoding=sys.getfilesystemencoding() - if not encoding: - encoding = preferred_encoding - p = [a] + list(p) - _unicode = False - for i in p: - if isinstance(i, unicode): - _unicode = True - break - p = [i.encode(encoding) if isinstance(i, unicode) else i for i in p] - - res = _join(*p) - if _unicode: - res = res.decode(encoding) - return res - - os.path.join = my_join def local_open(name, mode='r', bufsize=-1): ''' From 0a22a234cd80511a6c0eb7419ca536c36c0291d6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 6 Mar 2011 11:46:04 -0700 Subject: [PATCH 10/10] ... --- src/calibre/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 5ba1aa42de..fa9a8f2404 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -84,7 +84,7 @@ def sanitize_file_name(name, substitute='_', as_unicode=False): one = one.decode(filesystem_encoding) one = one.replace('..', substitute) # Windows doesn't like path components that end with a period - if one.endswith('.'): + if one and one[-1] in ('.', ' '): one = one[:-1]+'_' # Names starting with a period are hidden on Unix if one.startswith('.'):