mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit metadata dialog: Show a confirmation dialog on cancel if some changes have been made. Fixes #1786544 [[Enhancement] Cancelling out of edit metadata dialog box should prompt user whether to save changes](https://bugs.launchpad.net/calibre/+bug/1786544)
This commit is contained in:
parent
f8e4062d9b
commit
ed6184f628
@ -13,7 +13,7 @@ from PyQt5.Qt import (QApplication, QFontInfo, QSize, QWidget, QPlainTextEdit,
|
|||||||
QToolBar, QVBoxLayout, QAction, QIcon, Qt, QTabWidget, QUrl, QFormLayout,
|
QToolBar, QVBoxLayout, QAction, QIcon, Qt, QTabWidget, QUrl, QFormLayout,
|
||||||
QSyntaxHighlighter, QColor, QColorDialog, QMenu, QDialog, QLabel,
|
QSyntaxHighlighter, QColor, QColorDialog, QMenu, QDialog, QLabel,
|
||||||
QHBoxLayout, QKeySequence, QLineEdit, QDialogButtonBox, QPushButton,
|
QHBoxLayout, QKeySequence, QLineEdit, QDialogButtonBox, QPushButton,
|
||||||
QCheckBox)
|
pyqtSignal, QCheckBox)
|
||||||
from PyQt5.QtWebKitWidgets import QWebView, QWebPage
|
from PyQt5.QtWebKitWidgets import QWebView, QWebPage
|
||||||
try:
|
try:
|
||||||
from PyQt5 import sip
|
from PyQt5 import sip
|
||||||
@ -74,6 +74,8 @@ class BlockStyleAction(QAction): # {{{
|
|||||||
|
|
||||||
class EditorWidget(QWebView, LineEditECM): # {{{
|
class EditorWidget(QWebView, LineEditECM): # {{{
|
||||||
|
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QWebView.__init__(self, parent)
|
QWebView.__init__(self, parent)
|
||||||
self.base_url = None
|
self.base_url = None
|
||||||
@ -184,6 +186,7 @@ class EditorWidget(QWebView, LineEditECM): # {{{
|
|||||||
|
|
||||||
self.setHtml('')
|
self.setHtml('')
|
||||||
self.set_readonly(False)
|
self.set_readonly(False)
|
||||||
|
self.page().contentsChanged.connect(self.data_changed)
|
||||||
|
|
||||||
def update_link_action(self):
|
def update_link_action(self):
|
||||||
wac = self.pageAction(QWebPage.ToggleBold).isEnabled()
|
wac = self.pageAction(QWebPage.ToggleBold).isEnabled()
|
||||||
@ -660,6 +663,7 @@ class Highlighter(QSyntaxHighlighter):
|
|||||||
class Editor(QWidget): # {{{
|
class Editor(QWidget): # {{{
|
||||||
|
|
||||||
toolbar_prefs_name = None
|
toolbar_prefs_name = None
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None, one_line_toolbar=False, toolbar_prefs_name=None):
|
def __init__(self, parent=None, one_line_toolbar=False, toolbar_prefs_name=None):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
@ -671,6 +675,7 @@ class Editor(QWidget): # {{{
|
|||||||
t = getattr(self, 'toolbar%d'%i)
|
t = getattr(self, 'toolbar%d'%i)
|
||||||
t.setIconSize(QSize(18, 18))
|
t.setIconSize(QSize(18, 18))
|
||||||
self.editor = EditorWidget(self)
|
self.editor = EditorWidget(self)
|
||||||
|
self.editor.data_changed.connect(self.data_changed)
|
||||||
self.set_base_url = self.editor.set_base_url
|
self.set_base_url = self.editor.set_base_url
|
||||||
self.set_html = self.editor.set_html
|
self.set_html = self.editor.set_html
|
||||||
self.tabs = QTabWidget(self)
|
self.tabs = QTabWidget(self)
|
||||||
|
@ -25,12 +25,20 @@ from calibre.gui2.library.delegates import ClearingDoubleSpinBox, ClearingSpinBo
|
|||||||
from calibre.gui2.widgets2 import RatingEditor
|
from calibre.gui2.widgets2 import RatingEditor
|
||||||
|
|
||||||
|
|
||||||
|
def safe_disconnect(signal):
|
||||||
|
try:
|
||||||
|
signal.disconnect()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Base(object):
|
class Base(object):
|
||||||
|
|
||||||
def __init__(self, db, col_id, parent=None):
|
def __init__(self, db, col_id, parent=None):
|
||||||
self.db, self.col_id = db, col_id
|
self.db, self.col_id = db, col_id
|
||||||
self.col_metadata = db.custom_column_num_map[col_id]
|
self.col_metadata = db.custom_column_num_map[col_id]
|
||||||
self.initial_val = self.widgets = None
|
self.initial_val = self.widgets = None
|
||||||
|
self.signals_to_disconnect = []
|
||||||
self.setup_ui(parent)
|
self.setup_ui(parent)
|
||||||
|
|
||||||
def initialize(self, book_id):
|
def initialize(self, book_id):
|
||||||
@ -66,6 +74,12 @@ class Base(object):
|
|||||||
|
|
||||||
def break_cycles(self):
|
def break_cycles(self):
|
||||||
self.db = self.widgets = self.initial_val = None
|
self.db = self.widgets = self.initial_val = None
|
||||||
|
for signal in self.signals_to_disconnect:
|
||||||
|
safe_disconnect(signal)
|
||||||
|
self.signals_to_disconnect = []
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SimpleText(Base):
|
class SimpleText(Base):
|
||||||
@ -79,6 +93,10 @@ class SimpleText(Base):
|
|||||||
def getter(self):
|
def getter(self):
|
||||||
return self.widgets[1].text().strip()
|
return self.widgets[1].text().strip()
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self.widgets[1].textChanged.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self.widgets[1].textChanged)
|
||||||
|
|
||||||
|
|
||||||
class LongText(Base):
|
class LongText(Base):
|
||||||
|
|
||||||
@ -98,6 +116,10 @@ class LongText(Base):
|
|||||||
def getter(self):
|
def getter(self):
|
||||||
return self._tb.toPlainText()
|
return self._tb.toPlainText()
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self._tb.textChanged.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self._tb.textChanged)
|
||||||
|
|
||||||
|
|
||||||
class Bool(Base):
|
class Bool(Base):
|
||||||
|
|
||||||
@ -165,6 +187,10 @@ class Bool(Base):
|
|||||||
def set_to_cleared(self):
|
def set_to_cleared(self):
|
||||||
self.combobox.setCurrentIndex(2)
|
self.combobox.setCurrentIndex(2)
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self.combobox.currentTextChanged.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self.combobox.currentTextChanged)
|
||||||
|
|
||||||
|
|
||||||
class Int(Base):
|
class Int(Base):
|
||||||
|
|
||||||
@ -195,6 +221,10 @@ class Int(Base):
|
|||||||
self.setter(0)
|
self.setter(0)
|
||||||
self.was_none = to_what == self.widgets[1].minimum()
|
self.was_none = to_what == self.widgets[1].minimum()
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self.widgets[1].valueChanged.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self.widgets[1].valueChanged)
|
||||||
|
|
||||||
|
|
||||||
class Float(Int):
|
class Float(Int):
|
||||||
|
|
||||||
@ -223,6 +253,10 @@ class Rating(Base):
|
|||||||
def getter(self):
|
def getter(self):
|
||||||
return self.widgets[1].rating_value or None
|
return self.widgets[1].rating_value or None
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self.widgets[1].currentTextChanged.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self.widgets[1].currentTextChanged)
|
||||||
|
|
||||||
|
|
||||||
class DateTimeEdit(QDateTimeEdit):
|
class DateTimeEdit(QDateTimeEdit):
|
||||||
|
|
||||||
@ -302,6 +336,10 @@ class DateTime(Base):
|
|||||||
def normalize_ui_val(self, val):
|
def normalize_ui_val(self, val):
|
||||||
return as_utc(val) if val is not None else None
|
return as_utc(val) if val is not None else None
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self.widgets[1].dateTimeChanged.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self.widgets[1].dateTimeChanged)
|
||||||
|
|
||||||
|
|
||||||
class Comments(Base):
|
class Comments(Base):
|
||||||
|
|
||||||
@ -345,6 +383,10 @@ class Comments(Base):
|
|||||||
self._tb.tab = val
|
self._tb.tab = val
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self._tb.data_changed.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self._tb.data_changed)
|
||||||
|
|
||||||
|
|
||||||
class MultipleWidget(QWidget):
|
class MultipleWidget(QWidget):
|
||||||
|
|
||||||
@ -481,6 +523,14 @@ class Text(Base):
|
|||||||
if d.exec_() == TagEditor.Accepted:
|
if d.exec_() == TagEditor.Accepted:
|
||||||
self.setter(d.tags)
|
self.setter(d.tags)
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
if self.col_metadata['is_multiple']:
|
||||||
|
s = self.widgets[1].tags_box.currentTextChanged
|
||||||
|
else:
|
||||||
|
s = self.widgets[1].currentTextChanged
|
||||||
|
s.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(s)
|
||||||
|
|
||||||
|
|
||||||
class Series(Base):
|
class Series(Base):
|
||||||
|
|
||||||
@ -554,6 +604,11 @@ class Series(Base):
|
|||||||
val, s_index = self.current_val
|
val, s_index = self.current_val
|
||||||
mi.set('#' + self.col_metadata['label'], val, extra=s_index)
|
mi.set('#' + self.col_metadata['label'], val, extra=s_index)
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
for s in self.widgets[1].editTextChanged, self.widgets[3].valueChanged:
|
||||||
|
s.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(s)
|
||||||
|
|
||||||
|
|
||||||
class Enumeration(Base):
|
class Enumeration(Base):
|
||||||
|
|
||||||
@ -598,6 +653,10 @@ class Enumeration(Base):
|
|||||||
val = None
|
val = None
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
def connect_data_changed(self, slot):
|
||||||
|
self.widgets[1].currentIndexChanged.connect(slot)
|
||||||
|
self.signals_to_disconnect.append(self.widgets[1].currentIndexChanged)
|
||||||
|
|
||||||
|
|
||||||
def comments_factory(db, key, parent):
|
def comments_factory(db, key, parent):
|
||||||
fm = db.custom_column_num_map[key]
|
fm = db.custom_column_num_map[key]
|
||||||
|
@ -195,12 +195,14 @@ class TitleEdit(EnLineEdit, ToMetadataMixin):
|
|||||||
TITLE_ATTR = FIELD_NAME = 'title'
|
TITLE_ATTR = FIELD_NAME = 'title'
|
||||||
TOOLTIP = _('Change the title of this book')
|
TOOLTIP = _('Change the title of this book')
|
||||||
LABEL = _('&Title:')
|
LABEL = _('&Title:')
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.dialog = parent
|
self.dialog = parent
|
||||||
EnLineEdit.__init__(self, parent)
|
EnLineEdit.__init__(self, parent)
|
||||||
self.setToolTip(self.TOOLTIP)
|
self.setToolTip(self.TOOLTIP)
|
||||||
self.setWhatsThis(self.TOOLTIP)
|
self.setWhatsThis(self.TOOLTIP)
|
||||||
|
self.textChanged.connect(self.data_changed)
|
||||||
|
|
||||||
def get_default(self):
|
def get_default(self):
|
||||||
return _('Unknown')
|
return _('Unknown')
|
||||||
@ -331,6 +333,7 @@ class AuthorsEdit(EditWithComplete, ToMetadataMixin):
|
|||||||
TOOLTIP = ''
|
TOOLTIP = ''
|
||||||
LABEL = _('&Author(s):')
|
LABEL = _('&Author(s):')
|
||||||
FIELD_NAME = 'authors'
|
FIELD_NAME = 'authors'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent, manage_authors):
|
def __init__(self, parent, manage_authors):
|
||||||
self.dialog = parent
|
self.dialog = parent
|
||||||
@ -343,6 +346,7 @@ class AuthorsEdit(EditWithComplete, ToMetadataMixin):
|
|||||||
self.manage_authors_signal = manage_authors
|
self.manage_authors_signal = manage_authors
|
||||||
manage_authors.triggered.connect(self.manage_authors)
|
manage_authors.triggered.connect(self.manage_authors)
|
||||||
self.lineEdit().createStandardContextMenu = self.createStandardContextMenu
|
self.lineEdit().createStandardContextMenu = self.createStandardContextMenu
|
||||||
|
self.lineEdit().textChanged.connect(self.data_changed)
|
||||||
|
|
||||||
def createStandardContextMenu(self):
|
def createStandardContextMenu(self):
|
||||||
menu = QLineEdit.createStandardContextMenu(self.lineEdit())
|
menu = QLineEdit.createStandardContextMenu(self.lineEdit())
|
||||||
@ -444,6 +448,7 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
|
|||||||
'red, then the authors and this text do not match.')
|
'red, then the authors and this text do not match.')
|
||||||
LABEL = _('Author s&ort:')
|
LABEL = _('Author s&ort:')
|
||||||
FIELD_NAME = 'author_sort'
|
FIELD_NAME = 'author_sort'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent, authors_edit, autogen_button, db,
|
def __init__(self, parent, authors_edit, autogen_button, db,
|
||||||
copy_a_to_as_action, copy_as_to_a_action, a_to_as, as_to_a):
|
copy_a_to_as_action, copy_as_to_a_action, a_to_as, as_to_a):
|
||||||
@ -463,6 +468,7 @@ class AuthorSortEdit(EnLineEdit, ToMetadataMixin):
|
|||||||
|
|
||||||
self.authors_edit.editTextChanged.connect(self.update_state_and_val, type=Qt.QueuedConnection)
|
self.authors_edit.editTextChanged.connect(self.update_state_and_val, type=Qt.QueuedConnection)
|
||||||
self.textChanged.connect(self.update_state)
|
self.textChanged.connect(self.update_state)
|
||||||
|
self.textChanged.connect(self.data_changed)
|
||||||
|
|
||||||
self.autogen_button = autogen_button
|
self.autogen_button = autogen_button
|
||||||
self.copy_a_to_as_action = copy_a_to_as_action
|
self.copy_a_to_as_action = copy_a_to_as_action
|
||||||
@ -591,6 +597,7 @@ class SeriesEdit(EditWithComplete, ToMetadataMixin):
|
|||||||
TOOLTIP = _('List of known series. You can add new series.')
|
TOOLTIP = _('List of known series. You can add new series.')
|
||||||
LABEL = _('&Series:')
|
LABEL = _('&Series:')
|
||||||
FIELD_NAME = 'series'
|
FIELD_NAME = 'series'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
EditWithComplete.__init__(self, parent)
|
EditWithComplete.__init__(self, parent)
|
||||||
@ -602,6 +609,7 @@ class SeriesEdit(EditWithComplete, ToMetadataMixin):
|
|||||||
self.setWhatsThis(self.TOOLTIP)
|
self.setWhatsThis(self.TOOLTIP)
|
||||||
self.setEditable(True)
|
self.setEditable(True)
|
||||||
self.books_to_refresh = set([])
|
self.books_to_refresh = set([])
|
||||||
|
self.lineEdit().textChanged.connect(self.data_changed)
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def current_val(self):
|
def current_val(self):
|
||||||
@ -645,9 +653,11 @@ class SeriesIndexEdit(make_undoable(QDoubleSpinBox), ToMetadataMixin):
|
|||||||
TOOLTIP = ''
|
TOOLTIP = ''
|
||||||
LABEL = _('&Number:')
|
LABEL = _('&Number:')
|
||||||
FIELD_NAME = 'series_index'
|
FIELD_NAME = 'series_index'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent, series_edit):
|
def __init__(self, parent, series_edit):
|
||||||
super(SeriesIndexEdit, self).__init__(parent)
|
super(SeriesIndexEdit, self).__init__(parent)
|
||||||
|
self.valueChanged.connect(self.data_changed)
|
||||||
self.dialog = parent
|
self.dialog = parent
|
||||||
self.db = self.original_series_name = None
|
self.db = self.original_series_name = None
|
||||||
self.setMaximum(10000000)
|
self.setMaximum(10000000)
|
||||||
@ -795,11 +805,23 @@ class FormatList(_FormatList):
|
|||||||
|
|
||||||
class FormatsManager(QWidget):
|
class FormatsManager(QWidget):
|
||||||
|
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def changed(self):
|
||||||
|
return self._changed
|
||||||
|
|
||||||
|
@changed.setter
|
||||||
|
def changed(self, val):
|
||||||
|
self._changed = val
|
||||||
|
if val:
|
||||||
|
self.data_changed.emit()
|
||||||
|
|
||||||
def __init__(self, parent, copy_fmt):
|
def __init__(self, parent, copy_fmt):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
self.dialog = parent
|
self.dialog = parent
|
||||||
self.copy_fmt = copy_fmt
|
self.copy_fmt = copy_fmt
|
||||||
self.changed = False
|
self._changed = False
|
||||||
|
|
||||||
self.l = l = QGridLayout()
|
self.l = l = QGridLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
@ -1031,6 +1053,7 @@ class FormatsManager(QWidget):
|
|||||||
class Cover(ImageView): # {{{
|
class Cover(ImageView): # {{{
|
||||||
|
|
||||||
download_cover = pyqtSignal()
|
download_cover = pyqtSignal()
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
ImageView.__init__(self, parent, show_size_pref_name='edit_metadata_cover_widget', default_show_size=True)
|
ImageView.__init__(self, parent, show_size_pref_name='edit_metadata_cover_widget', default_show_size=True)
|
||||||
@ -1202,6 +1225,7 @@ class Cover(ImageView): # {{{
|
|||||||
tt = _('Cover size: %(width)d x %(height)d pixels') % \
|
tt = _('Cover size: %(width)d x %(height)d pixels') % \
|
||||||
dict(width=pm.width(), height=pm.height())
|
dict(width=pm.width(), height=pm.height())
|
||||||
self.setToolTip(tt)
|
self.setToolTip(tt)
|
||||||
|
self.data_changed.emit()
|
||||||
|
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
@ -1245,6 +1269,7 @@ class CommentsEdit(Editor, ToMetadataMixin): # {{{
|
|||||||
val = comments_to_html(val)
|
val = comments_to_html(val)
|
||||||
self.set_html(val, self.allow_undo)
|
self.set_html(val, self.allow_undo)
|
||||||
self.wyswyg_dirtied()
|
self.wyswyg_dirtied()
|
||||||
|
self.data_changed.emit()
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
def initialize(self, db, id_):
|
def initialize(self, db, id_):
|
||||||
@ -1265,11 +1290,13 @@ class RatingEdit(RatingEditor, ToMetadataMixin): # {{{
|
|||||||
LABEL = _('&Rating:')
|
LABEL = _('&Rating:')
|
||||||
TOOLTIP = _('Rating of this book. 0-5 stars')
|
TOOLTIP = _('Rating of this book. 0-5 stars')
|
||||||
FIELD_NAME = 'rating'
|
FIELD_NAME = 'rating'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(RatingEdit, self).__init__(parent)
|
super(RatingEdit, self).__init__(parent)
|
||||||
self.setToolTip(self.TOOLTIP)
|
self.setToolTip(self.TOOLTIP)
|
||||||
self.setWhatsThis(self.TOOLTIP)
|
self.setWhatsThis(self.TOOLTIP)
|
||||||
|
self.currentTextChanged.connect(self.data_changed)
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def current_val(self):
|
def current_val(self):
|
||||||
@ -1301,9 +1328,11 @@ class TagsEdit(EditWithComplete, ToMetadataMixin): # {{{
|
|||||||
'useful while searching. <br><br>They can be any words '
|
'useful while searching. <br><br>They can be any words '
|
||||||
'or phrases, separated by commas.')
|
'or phrases, separated by commas.')
|
||||||
FIELD_NAME = 'tags'
|
FIELD_NAME = 'tags'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
EditWithComplete.__init__(self, parent)
|
EditWithComplete.__init__(self, parent)
|
||||||
|
self.currentTextChanged.connect(self.data_changed)
|
||||||
self.lineEdit().setMaxLength(655360) # see https://bugs.launchpad.net/bugs/1630944
|
self.lineEdit().setMaxLength(655360) # see https://bugs.launchpad.net/bugs/1630944
|
||||||
self.books_to_refresh = set([])
|
self.books_to_refresh = set([])
|
||||||
self.setToolTip(self.TOOLTIP)
|
self.setToolTip(self.TOOLTIP)
|
||||||
@ -1366,9 +1395,11 @@ class LanguagesEdit(LE, ToMetadataMixin): # {{{
|
|||||||
LABEL = _('&Languages:')
|
LABEL = _('&Languages:')
|
||||||
TOOLTIP = _('A comma separated list of languages for this book')
|
TOOLTIP = _('A comma separated list of languages for this book')
|
||||||
FIELD_NAME = 'languages'
|
FIELD_NAME = 'languages'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
LE.__init__(self, *args, **kwargs)
|
LE.__init__(self, *args, **kwargs)
|
||||||
|
self.textChanged.connect(self.data_changed)
|
||||||
self.setToolTip(self.TOOLTIP)
|
self.setToolTip(self.TOOLTIP)
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
@ -1458,11 +1489,13 @@ class IdentifiersEdit(QLineEdit, ToMetadataMixin):
|
|||||||
'For example: \n\n%s\n\nIf an identifier value contains a comma, you can use the | character to represent it.')%(
|
'For example: \n\n%s\n\nIf an identifier value contains a comma, you can use the | character to represent it.')%(
|
||||||
'isbn:1565927249, doi:10.1000/182, amazon:1565927249')
|
'isbn:1565927249, doi:10.1000/182, amazon:1565927249')
|
||||||
FIELD_NAME = 'identifiers'
|
FIELD_NAME = 'identifiers'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QLineEdit.__init__(self, parent)
|
QLineEdit.__init__(self, parent)
|
||||||
self.pat = re.compile(r'[^0-9a-zA-Z]')
|
self.pat = re.compile(r'[^0-9a-zA-Z]')
|
||||||
self.textChanged.connect(self.validate)
|
self.textChanged.connect(self.validate)
|
||||||
|
self.textChanged.connect(self.data_changed)
|
||||||
|
|
||||||
def contextMenuEvent(self, ev):
|
def contextMenuEvent(self, ev):
|
||||||
m = self.createStandardContextMenu()
|
m = self.createStandardContextMenu()
|
||||||
@ -1631,9 +1664,11 @@ class ISBNDialog(QDialog): # {{{
|
|||||||
class PublisherEdit(EditWithComplete, ToMetadataMixin): # {{{
|
class PublisherEdit(EditWithComplete, ToMetadataMixin): # {{{
|
||||||
LABEL = _('&Publisher:')
|
LABEL = _('&Publisher:')
|
||||||
FIELD_NAME = 'publisher'
|
FIELD_NAME = 'publisher'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
EditWithComplete.__init__(self, parent)
|
EditWithComplete.__init__(self, parent)
|
||||||
|
self.currentTextChanged.connect(self.data_changed)
|
||||||
self.set_separator(None)
|
self.set_separator(None)
|
||||||
self.setSizeAdjustPolicy(
|
self.setSizeAdjustPolicy(
|
||||||
self.AdjustToMinimumContentsLengthWithIcon)
|
self.AdjustToMinimumContentsLengthWithIcon)
|
||||||
@ -1694,11 +1729,13 @@ class DateEdit(make_undoable(QDateTimeEdit), ToMetadataMixin):
|
|||||||
FMT = 'dd MMM yyyy hh:mm:ss'
|
FMT = 'dd MMM yyyy hh:mm:ss'
|
||||||
ATTR = FIELD_NAME = 'timestamp'
|
ATTR = FIELD_NAME = 'timestamp'
|
||||||
TWEAK = 'gui_timestamp_display_format'
|
TWEAK = 'gui_timestamp_display_format'
|
||||||
|
data_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent, create_clear_button=True):
|
def __init__(self, parent, create_clear_button=True):
|
||||||
super(DateEdit, self).__init__(parent)
|
super(DateEdit, self).__init__(parent)
|
||||||
self.setToolTip(self.TOOLTIP)
|
self.setToolTip(self.TOOLTIP)
|
||||||
self.setWhatsThis(self.TOOLTIP)
|
self.setWhatsThis(self.TOOLTIP)
|
||||||
|
self.dateTimeChanged.connect(self.data_changed)
|
||||||
fmt = tweaks[self.TWEAK]
|
fmt = tweaks[self.TWEAK]
|
||||||
if fmt is None:
|
if fmt is None:
|
||||||
fmt = self.FMT
|
fmt = self.FMT
|
||||||
|
@ -17,6 +17,7 @@ from PyQt5.Qt import (Qt, QVBoxLayout, QHBoxLayout, QWidget, QPushButton,
|
|||||||
QSizePolicy, QFrame, QSize, QKeySequence, QMenu, QShortcut, QDialog)
|
QSizePolicy, QFrame, QSize, QKeySequence, QMenu, QShortcut, QDialog)
|
||||||
|
|
||||||
from calibre.constants import isosx
|
from calibre.constants import isosx
|
||||||
|
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import authors_to_string, string_to_authors
|
||||||
from calibre.gui2 import error_dialog, gprefs, pixmap_to_data
|
from calibre.gui2 import error_dialog, gprefs, pixmap_to_data
|
||||||
from calibre.gui2.metadata.basic_widgets import (TitleEdit, AuthorsEdit,
|
from calibre.gui2.metadata.basic_widgets import (TitleEdit, AuthorsEdit,
|
||||||
@ -56,6 +57,7 @@ class MetadataSingleDialogBase(QDialog):
|
|||||||
|
|
||||||
def __init__(self, db, parent=None, editing_multiple=False):
|
def __init__(self, db, parent=None, editing_multiple=False):
|
||||||
self.db = db
|
self.db = db
|
||||||
|
self.was_data_edited = False
|
||||||
self.changed = set()
|
self.changed = set()
|
||||||
self.books_to_refresh = set()
|
self.books_to_refresh = set()
|
||||||
self.rows_to_refresh = set()
|
self.rows_to_refresh = set()
|
||||||
@ -292,6 +294,8 @@ class MetadataSingleDialogBase(QDialog):
|
|||||||
self.config_metadata_button.clicked.connect(self.configure_metadata)
|
self.config_metadata_button.clicked.connect(self.configure_metadata)
|
||||||
self.config_metadata_button.setToolTip(
|
self.config_metadata_button.setToolTip(
|
||||||
_('Change how calibre downloads metadata'))
|
_('Change how calibre downloads metadata'))
|
||||||
|
for w in self.basic_metadata_widgets:
|
||||||
|
w.data_changed.connect(self.data_changed)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -320,6 +324,7 @@ class MetadataSingleDialogBase(QDialog):
|
|||||||
two_column=self.cc_two_column)
|
two_column=self.cc_two_column)
|
||||||
self.__custom_col_layouts = [layout]
|
self.__custom_col_layouts = [layout]
|
||||||
for widget in self.custom_metadata_widgets:
|
for widget in self.custom_metadata_widgets:
|
||||||
|
widget.connect_data_changed(self.data_changed)
|
||||||
if isinstance(widget, Comments):
|
if isinstance(widget, Comments):
|
||||||
self.comments_edit_state_at_apply[widget] = None
|
self.comments_edit_state_at_apply[widget] = None
|
||||||
# }}}
|
# }}}
|
||||||
@ -353,6 +358,9 @@ class MetadataSingleDialogBase(QDialog):
|
|||||||
def do_layout(self):
|
def do_layout(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def data_changed(self):
|
||||||
|
self.was_data_edited = True
|
||||||
|
|
||||||
def __call__(self, id_):
|
def __call__(self, id_):
|
||||||
self.book_id = id_
|
self.book_id = id_
|
||||||
self.books_to_refresh = set([])
|
self.books_to_refresh = set([])
|
||||||
@ -363,6 +371,7 @@ class MetadataSingleDialogBase(QDialog):
|
|||||||
widget.initialize(id_)
|
widget.initialize(id_)
|
||||||
if callable(self.set_current_callback):
|
if callable(self.set_current_callback):
|
||||||
self.set_current_callback(id_)
|
self.set_current_callback(id_)
|
||||||
|
self.was_data_edited = False
|
||||||
# Commented out as it doesn't play nice with Next, Prev buttons
|
# Commented out as it doesn't play nice with Next, Prev buttons
|
||||||
# self.fetch_metadata_button.setFocus(Qt.OtherFocusReason)
|
# self.fetch_metadata_button.setFocus(Qt.OtherFocusReason)
|
||||||
|
|
||||||
@ -620,6 +629,10 @@ class MetadataSingleDialogBase(QDialog):
|
|||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
if self.was_data_edited and not confirm(
|
||||||
|
title=_('Are you sure?'), name='confirm-cancel-edit-single-metadata', msg=_(
|
||||||
|
'You will lose all unsaved changes, are you sure?'), parent=self):
|
||||||
|
return
|
||||||
QDialog.reject(self)
|
QDialog.reject(self)
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user