On windows when changing title or author via the main book list, handle the case of one of the books files being open in naother program more gracefully. Fixes #880585 (Changing the case of title generates "OSError:[Errno 13] Permission Denied...")

This commit is contained in:
Kovid Goyal 2011-11-10 12:35:24 +05:30
parent 794f010dc0
commit 36e40ee553
2 changed files with 75 additions and 56 deletions

View File

@ -5,13 +5,13 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import functools, re, os, traceback import functools, re, os, traceback, errno
from collections import defaultdict from collections import defaultdict
from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
QModelIndex, QVariant, QDateTime, QColor) QModelIndex, QVariant, QDateTime, QColor)
from calibre.gui2 import NONE, UNDEFINED_QDATETIME from calibre.gui2 import NONE, UNDEFINED_QDATETIME, error_dialog
from calibre.utils.pyparsing import ParseException from calibre.utils.pyparsing import ParseException
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
from calibre.ebooks.metadata.book.base import SafeFormat from calibre.ebooks.metadata.book.base import SafeFormat
@ -851,59 +851,78 @@ class BooksModel(QAbstractTableModel): # {{{
def setData(self, index, value, role): def setData(self, index, value, role):
if role == Qt.EditRole: if role == Qt.EditRole:
row, col = index.row(), index.column() from calibre.gui2.ui import get_gui
column = self.column_map[col] try:
if self.is_custom_column(column): return self._set_data(index, value)
if not self.set_custom_column_data(row, column, value): except (IOError, OSError) as err:
return False if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
else: import traceback
if column not in self.editable_cols: error_dialog(get_gui(), _('Permission denied'),
return False _('Could not change the on disk location of this'
val = int(value.toInt()[0]) if column == 'rating' else \ ' book. Is it open in another program?'),
value.toDateTime() if column in ('timestamp', 'pubdate') else \ det_msg=traceback.format_exc(), show=True)
unicode(value.toString()).strip() except:
id = self.db.id(row) import traceback
books_to_refresh = set([id]) traceback.print_exc()
if column == 'rating': error_dialog(get_gui(), _('Failed to set data'),
val = 0 if val < 0 else 5 if val > 5 else val _('Could not set data, click Show Details to see why.'),
val *= 2 det_msg=traceback.format_exc(), show=True)
self.db.set_rating(id, val) return False
elif column == 'series':
val = val.strip() def _set_data(self, index, value):
if not val: row, col = index.row(), index.column()
books_to_refresh |= self.db.set_series(id, val, column = self.column_map[col]
allow_case_change=True) if self.is_custom_column(column):
self.db.set_series_index(id, 1.0) if not self.set_custom_column_data(row, column, value):
else: return False
pat = re.compile(r'\[([.0-9]+)\]') else:
match = pat.search(val) if column not in self.editable_cols:
if match is not None: return False
self.db.set_series_index(id, float(match.group(1))) val = (int(value.toInt()[0]) if column == 'rating' else
val = pat.sub('', val).strip() value.toDateTime() if column in ('timestamp', 'pubdate')
elif val: else unicode(value.toString()).strip())
if tweaks['series_index_auto_increment'] != 'const': id = self.db.id(row)
ni = self.db.get_next_series_num_for(val) books_to_refresh = set([id])
if ni != 1: if column == 'rating':
self.db.set_series_index(id, ni) val = 0 if val < 0 else 5 if val > 5 else val
if val: val *= 2
books_to_refresh |= self.db.set_series(id, val, self.db.set_rating(id, val)
allow_case_change=True) elif column == 'series':
elif column == 'timestamp': val = val.strip()
if val.isNull() or not val.isValid(): if not val:
return False books_to_refresh |= self.db.set_series(id, val,
self.db.set_timestamp(id, qt_to_dt(val, as_utc=False))
elif column == 'pubdate':
if val.isNull() or not val.isValid():
return False
self.db.set_pubdate(id, qt_to_dt(val, as_utc=False))
elif column == 'languages':
val = val.split(',')
self.db.set_languages(id, val)
else:
books_to_refresh |= self.db.set(row, column, val,
allow_case_change=True) allow_case_change=True)
self.refresh_ids(list(books_to_refresh), row) self.db.set_series_index(id, 1.0)
self.dataChanged.emit(index, index) else:
pat = re.compile(r'\[([.0-9]+)\]')
match = pat.search(val)
if match is not None:
self.db.set_series_index(id, float(match.group(1)))
val = pat.sub('', val).strip()
elif val:
if tweaks['series_index_auto_increment'] != 'const':
ni = self.db.get_next_series_num_for(val)
if ni != 1:
self.db.set_series_index(id, ni)
if val:
books_to_refresh |= self.db.set_series(id, val,
allow_case_change=True)
elif column == 'timestamp':
if val.isNull() or not val.isValid():
return False
self.db.set_timestamp(id, qt_to_dt(val, as_utc=False))
elif column == 'pubdate':
if val.isNull() or not val.isValid():
return False
self.db.set_pubdate(id, qt_to_dt(val, as_utc=False))
elif column == 'languages':
val = val.split(',')
self.db.set_languages(id, val)
else:
books_to_refresh |= self.db.set(row, column, val,
allow_case_change=True)
self.refresh_ids(list(books_to_refresh), row)
self.dataChanged.emit(index, index)
return True return True
# }}} # }}}

View File

@ -440,8 +440,8 @@ class MetadataSingleDialogBase(ResizableDialog):
return False return False
self.books_to_refresh |= getattr(widget, 'books_to_refresh', self.books_to_refresh |= getattr(widget, 'books_to_refresh',
set([])) set([]))
except IOError as err: except (IOError, OSError) as err:
if err.errno == errno.EACCES: # Permission denied if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
import traceback import traceback
fname = err.filename if err.filename else 'file' fname = err.filename if err.filename else 'file'
error_dialog(self, _('Permission denied'), error_dialog(self, _('Permission denied'),