mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow copy/pasting dates from columns having different date display formats. Fixes #1885004 [Enhancement Request: Copy & paste dates between fields of unlike formats](https://bugs.launchpad.net/calibre/+bug/1885004)
Also consolidate all date/time editor to use the same underlying widget.
This commit is contained in:
parent
c6b4140231
commit
9b3c57062b
@ -9,10 +9,10 @@ __docformat__ = 'restructuredtext en'
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from PyQt5.Qt import (QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateTimeEdit,
|
||||
from PyQt5.Qt import (QComboBox, QLabel, QSpinBox, QDoubleSpinBox,
|
||||
QDateTime, QGroupBox, QVBoxLayout, QSizePolicy, QGridLayout, QUrl,
|
||||
QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, QLineEdit,
|
||||
QPushButton, QMessageBox, QToolButton, Qt, QPlainTextEdit)
|
||||
QPushButton, QMessageBox, QToolButton, QPlainTextEdit)
|
||||
|
||||
from calibre.utils.date import qt_to_dt, now, as_local_time, as_utc, internal_iso_format_string
|
||||
from calibre.gui2.complete2 import EditWithComplete
|
||||
@ -23,7 +23,7 @@ from calibre.utils.config import tweaks
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.library.comments import comments_to_html
|
||||
from calibre.gui2.library.delegates import ClearingDoubleSpinBox, ClearingSpinBox
|
||||
from calibre.gui2.widgets2 import RatingEditor
|
||||
from calibre.gui2.widgets2 import RatingEditor, DateTimeEdit as DateTimeEditBase
|
||||
from polyglot.builtins import unicode_type
|
||||
|
||||
|
||||
@ -275,15 +275,15 @@ class Rating(Base):
|
||||
self.signals_to_disconnect.append(self.widgets[1].currentTextChanged)
|
||||
|
||||
|
||||
class DateTimeEdit(QDateTimeEdit):
|
||||
class DateTimeEdit(DateTimeEditBase):
|
||||
|
||||
def focusInEvent(self, x):
|
||||
self.setSpecialValueText('')
|
||||
QDateTimeEdit.focusInEvent(self, x)
|
||||
DateTimeEditBase.focusInEvent(self, x)
|
||||
|
||||
def focusOutEvent(self, x):
|
||||
self.setSpecialValueText(_('Undefined'))
|
||||
QDateTimeEdit.focusOutEvent(self, x)
|
||||
DateTimeEditBase.focusOutEvent(self, x)
|
||||
|
||||
def set_to_today(self):
|
||||
self.setDateTime(now())
|
||||
@ -292,16 +292,6 @@ class DateTimeEdit(QDateTimeEdit):
|
||||
self.setDateTime(now())
|
||||
self.setDateTime(UNDEFINED_QDATETIME)
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
if ev.key() == Qt.Key_Minus:
|
||||
ev.accept()
|
||||
self.setDateTime(self.minimumDateTime())
|
||||
elif ev.key() == Qt.Key_Equal:
|
||||
ev.accept()
|
||||
self.setDateTime(QDateTime.currentDateTime())
|
||||
else:
|
||||
return QDateTimeEdit.keyPressEvent(self, ev)
|
||||
|
||||
|
||||
class DateTime(Base):
|
||||
|
||||
|
@ -26,7 +26,6 @@ from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||
from calibre.gui2.dialogs.template_line_editor import TemplateLineEditor
|
||||
from calibre.gui2.metadata.basic_widgets import CalendarWidget
|
||||
from calibre.utils.config import JSONConfig, dynamic, prefs, tweaks
|
||||
from calibre.utils.date import qt_to_dt, internal_iso_format_string
|
||||
from calibre.utils.icu import capitalize, sort_key
|
||||
@ -503,10 +502,6 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.series.editTextChanged.connect(self.series_changed)
|
||||
self.tag_editor_button.clicked.connect(self.tag_editor)
|
||||
self.autonumber_series.stateChanged[int].connect(self.auto_number_changed)
|
||||
self.pubdate.setMinimumDateTime(UNDEFINED_QDATETIME)
|
||||
self.pubdate_cw = CalendarWidget(self.pubdate)
|
||||
self.pubdate_cw.setVerticalHeaderFormat(self.pubdate_cw.NoVerticalHeader)
|
||||
self.pubdate.setCalendarWidget(self.pubdate_cw)
|
||||
pubdate_format = tweaks['gui_pubdate_display_format']
|
||||
if pubdate_format == 'iso':
|
||||
pubdate_format = internal_iso_format_string()
|
||||
@ -516,10 +511,6 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.clear_pubdate_button.clicked.connect(self.clear_pubdate)
|
||||
self.pubdate.dateTimeChanged.connect(self.do_apply_pubdate)
|
||||
self.adddate.setDateTime(QDateTime.currentDateTime())
|
||||
self.adddate.setMinimumDateTime(UNDEFINED_QDATETIME)
|
||||
self.adddate_cw = CalendarWidget(self.adddate)
|
||||
self.adddate_cw.setVerticalHeaderFormat(self.adddate_cw.NoVerticalHeader)
|
||||
self.adddate.setCalendarWidget(self.adddate_cw)
|
||||
adddate_format = tweaks['gui_timestamp_display_format']
|
||||
if adddate_format == 'iso':
|
||||
adddate_format = internal_iso_format_string()
|
||||
|
@ -401,7 +401,7 @@ from the value in the box</string>
|
||||
<item row="10" column="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QDateTimeEdit" name="pubdate">
|
||||
<widget class="DateTimeEdit" name="pubdate">
|
||||
<property name="displayFormat">
|
||||
<string>MMM yyyy</string>
|
||||
</property>
|
||||
@ -445,7 +445,7 @@ from the value in the box</string>
|
||||
<item row="9" column="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QDateTimeEdit" name="adddate">
|
||||
<widget class="DateTimeEdit" name="adddate">
|
||||
<property name="displayFormat">
|
||||
<string>d MMM yyyy</string>
|
||||
</property>
|
||||
@ -726,8 +726,8 @@ for e.g., EPUB to EPUB, calibre saves the original EPUB
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>935</width>
|
||||
<height>639</height>
|
||||
<width>777</width>
|
||||
<height>388</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="vargrid">
|
||||
@ -1205,8 +1205,8 @@ not multiple and the destination field is multiple</string>
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>917</width>
|
||||
<height>319</height>
|
||||
<width>203</width>
|
||||
<height>70</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="testgrid">
|
||||
@ -1329,6 +1329,11 @@ is completed. This can be slow on large libraries.</string>
|
||||
<extends>QComboBox</extends>
|
||||
<header>calibre/gui2/widgets2.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>DateTimeEdit</class>
|
||||
<extends>QDateTimeEdit</extends>
|
||||
<header>calibre/gui2/widgets2.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>central_widget</tabstop>
|
||||
|
@ -17,7 +17,7 @@ from calibre.ebooks.metadata import rating_to_stars
|
||||
from calibre.gui2 import UNDEFINED_QDATETIME, rating_font
|
||||
from calibre.constants import iswindows
|
||||
from calibre.gui2.widgets import EnLineEdit
|
||||
from calibre.gui2.widgets2 import populate_standard_spinbox_context_menu, RatingEditor
|
||||
from calibre.gui2.widgets2 import populate_standard_spinbox_context_menu, RatingEditor, DateTimeEdit as DateTimeEditBase
|
||||
from calibre.gui2.complete2 import EditWithComplete
|
||||
from calibre.utils.date import now, format_date, qt_to_dt, is_date_undefined, internal_iso_format_string
|
||||
|
||||
@ -112,43 +112,15 @@ class UpdateEditorGeometry(object):
|
||||
editor.setGeometry(initial_geometry)
|
||||
|
||||
|
||||
class DateTimeEdit(QDateTimeEdit): # {{{
|
||||
class DateTimeEdit(DateTimeEditBase): # {{{
|
||||
|
||||
def __init__(self, parent, format_):
|
||||
QDateTimeEdit.__init__(self, parent)
|
||||
DateTimeEditBase.__init__(self, parent)
|
||||
self.setFrame(False)
|
||||
self.setMinimumDateTime(UNDEFINED_QDATETIME)
|
||||
self.setSpecialValueText(_('Undefined'))
|
||||
self.setCalendarPopup(True)
|
||||
if format_ == 'iso':
|
||||
format_ = internal_iso_format_string()
|
||||
self.setDisplayFormat(format_)
|
||||
|
||||
def contextMenuEvent(self, ev):
|
||||
m = QMenu(self)
|
||||
m.addAction(_('Set date to undefined') + '\t' + QKeySequence(Qt.Key_Minus).toString(QKeySequence.NativeText),
|
||||
self.clear_date)
|
||||
m.addAction(_('Set date to today') + '\t' + QKeySequence(Qt.Key_Equal).toString(QKeySequence.NativeText),
|
||||
self.today_date)
|
||||
m.addSeparator()
|
||||
populate_standard_spinbox_context_menu(self, m)
|
||||
m.popup(ev.globalPos())
|
||||
|
||||
def today_date(self):
|
||||
self.setDateTime(QDateTime.currentDateTime())
|
||||
|
||||
def clear_date(self):
|
||||
self.setDateTime(UNDEFINED_QDATETIME)
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
if ev.key() == Qt.Key_Minus:
|
||||
ev.accept()
|
||||
self.clear_date()
|
||||
elif ev.key() == Qt.Key_Equal:
|
||||
self.today_date()
|
||||
ev.accept()
|
||||
else:
|
||||
return QDateTimeEdit.keyPressEvent(self, ev)
|
||||
# }}}
|
||||
|
||||
# Number Editor {{{
|
||||
|
@ -10,20 +10,20 @@ import textwrap, re, os, shutil, weakref
|
||||
from datetime import date, datetime
|
||||
|
||||
from PyQt5.Qt import (
|
||||
Qt, QDateTimeEdit, pyqtSignal, QMessageBox, QIcon, QToolButton, QWidget,
|
||||
Qt, pyqtSignal, QMessageBox, QIcon, QToolButton, QWidget,
|
||||
QLabel, QGridLayout, QApplication, QDoubleSpinBox, QListWidgetItem, QSize,
|
||||
QPixmap, QDialog, QMenu, QLineEdit, QSizePolicy, QKeySequence,
|
||||
QDialogButtonBox, QAction, QCalendarWidget, QDate, QDateTime, QUndoCommand,
|
||||
QDialogButtonBox, QAction, QDateTime, QUndoCommand,
|
||||
QUndoStack, QVBoxLayout, QPlainTextEdit, QUrl)
|
||||
|
||||
from calibre.gui2.widgets import EnLineEdit, FormatList as _FormatList, ImageView
|
||||
from calibre.gui2.widgets2 import access_key, populate_standard_spinbox_context_menu, RightClickButton, Dialog, RatingEditor
|
||||
from calibre.gui2.widgets2 import access_key, populate_standard_spinbox_context_menu, RightClickButton, Dialog, RatingEditor, DateTimeEdit
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.utils.config import tweaks, prefs
|
||||
from calibre.ebooks.metadata import (
|
||||
title_sort, string_to_authors, check_isbn, authors_to_sort_string)
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.gui2 import (file_icon_provider, UNDEFINED_QDATETIME,
|
||||
from calibre.gui2 import (file_icon_provider,
|
||||
choose_files, error_dialog, choose_images, gprefs)
|
||||
from calibre.gui2.complete2 import EditWithComplete
|
||||
from calibre.utils.date import (
|
||||
@ -1797,14 +1797,7 @@ class PublisherEdit(EditWithComplete, ToMetadataMixin): # {{{
|
||||
# DateEdit {{{
|
||||
|
||||
|
||||
class CalendarWidget(QCalendarWidget):
|
||||
|
||||
def showEvent(self, ev):
|
||||
if self.selectedDate().year() == UNDEFINED_DATE.year:
|
||||
self.setSelectedDate(QDate.currentDate())
|
||||
|
||||
|
||||
class DateEdit(make_undoable(QDateTimeEdit), ToMetadataMixin):
|
||||
class DateEdit(make_undoable(DateTimeEdit), ToMetadataMixin):
|
||||
|
||||
TOOLTIP = ''
|
||||
LABEL = _('&Date:')
|
||||
@ -1824,12 +1817,6 @@ class DateEdit(make_undoable(QDateTimeEdit), ToMetadataMixin):
|
||||
elif fmt == 'iso':
|
||||
fmt = internal_iso_format_string()
|
||||
self.setDisplayFormat(fmt)
|
||||
self.setCalendarPopup(True)
|
||||
self.cw = CalendarWidget(self)
|
||||
self.cw.setVerticalHeaderFormat(self.cw.NoVerticalHeader)
|
||||
self.setCalendarWidget(self.cw)
|
||||
self.setMinimumDateTime(UNDEFINED_QDATETIME)
|
||||
self.setSpecialValueText(_('Undefined'))
|
||||
if create_clear_button:
|
||||
self.clear_button = QToolButton(parent)
|
||||
self.clear_button.setIcon(QIcon(I('trash.png')))
|
||||
@ -1867,13 +1854,7 @@ class DateEdit(make_undoable(QDateTimeEdit), ToMetadataMixin):
|
||||
return o != c
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
if ev.key() == Qt.Key_Minus:
|
||||
ev.accept()
|
||||
self.setDateTime(self.minimumDateTime())
|
||||
elif ev.key() == Qt.Key_Equal:
|
||||
ev.accept()
|
||||
self.setDateTime(QDateTime.currentDateTime())
|
||||
elif ev.key() == Qt.Key_Up and is_date_undefined(self.current_val):
|
||||
if ev.key() == Qt.Key_Up and is_date_undefined(self.current_val):
|
||||
self.setDateTime(QDateTime.currentDateTime())
|
||||
elif ev.key() == Qt.Key_Tab and is_date_undefined(self.current_val):
|
||||
ev.ignore()
|
||||
|
@ -7,18 +7,20 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
||||
import weakref
|
||||
|
||||
from PyQt5.Qt import (
|
||||
QApplication, QCheckBox, QColor, QColorDialog, QComboBox, QDialog,
|
||||
QDialogButtonBox, QFont, QFontInfo, QFontMetrics, QIcon, QKeySequence, QLabel,
|
||||
QLayout, QPalette, QPixmap, QPoint, QPushButton, QRect, QScrollArea, QSize,
|
||||
QApplication, QByteArray, QCalendarWidget, QCheckBox, QColor, QColorDialog,
|
||||
QComboBox, QDate, QDateTime, QDateTimeEdit, QDialog, QDialogButtonBox, QFont,
|
||||
QFontInfo, QFontMetrics, QIcon, QKeySequence, QLabel, QLayout, QLineEdit, QMenu,
|
||||
QMimeData, QPalette, QPixmap, QPoint, QPushButton, QRect, QScrollArea, QSize,
|
||||
QSizePolicy, QStyle, QStyledItemDelegate, Qt, QTabWidget, QTextBrowser,
|
||||
QToolButton, QUndoCommand, QUndoStack, QWidget, pyqtSignal, QByteArray
|
||||
QToolButton, QUndoCommand, QUndoStack, QWidget, pyqtSignal, pyqtSlot
|
||||
)
|
||||
|
||||
from calibre.ebooks.metadata import rating_to_stars
|
||||
from calibre.gui2 import gprefs, rating_font
|
||||
from calibre.gui2 import UNDEFINED_QDATETIME, gprefs, rating_font
|
||||
from calibre.gui2.complete2 import EditWithComplete, LineEdit
|
||||
from calibre.gui2.widgets import history
|
||||
from calibre.utils.config_base import tweaks
|
||||
from calibre.utils.date import UNDEFINED_DATE
|
||||
from polyglot.builtins import unicode_type
|
||||
from polyglot.functools import lru_cache
|
||||
|
||||
@ -555,6 +557,93 @@ def to_plain_text(self):
|
||||
return ans.rstrip('\0')
|
||||
|
||||
|
||||
class LineEditForDateTimeEdit(QLineEdit):
|
||||
|
||||
date_time_pasted = pyqtSignal(object)
|
||||
date_time_copied = pyqtSignal(object)
|
||||
MIME_TYPE = 'application/x-calibre-datetime-value'
|
||||
|
||||
@pyqtSlot()
|
||||
def copy(self):
|
||||
self.date_time_copied.emit(self.selectedText())
|
||||
|
||||
@pyqtSlot()
|
||||
def paste(self):
|
||||
md = QApplication.instance().clipboard().mimeData()
|
||||
if md.hasFormat(self.MIME_TYPE):
|
||||
self.date_time_pasted.emit(QDateTime.fromString(md.data(self.MIME_TYPE).data().decode('ascii'), Qt.ISODate))
|
||||
else:
|
||||
QLineEdit.paste(self)
|
||||
|
||||
|
||||
class CalendarWidget(QCalendarWidget):
|
||||
|
||||
def showEvent(self, ev):
|
||||
if self.selectedDate().year() == UNDEFINED_DATE.year:
|
||||
self.setSelectedDate(QDate.currentDate())
|
||||
|
||||
|
||||
class DateTimeEdit(QDateTimeEdit):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QDateTimeEdit.__init__(self, parent)
|
||||
le = LineEditForDateTimeEdit(self)
|
||||
self.setLineEdit(le)
|
||||
le.date_time_pasted.connect(self.date_time_pasted, type=Qt.QueuedConnection)
|
||||
le.date_time_copied.connect(self.date_time_copied, type=Qt.QueuedConnection)
|
||||
self.setMinimumDateTime(UNDEFINED_QDATETIME)
|
||||
self.setCalendarPopup(True)
|
||||
self.cw = CalendarWidget(self)
|
||||
self.cw.setVerticalHeaderFormat(self.cw.NoVerticalHeader)
|
||||
self.setCalendarWidget(self.cw)
|
||||
self.setSpecialValueText(_('Undefined'))
|
||||
|
||||
def date_time_copied(self, text):
|
||||
md = QMimeData()
|
||||
md.setText(text or self.dateTime().toString())
|
||||
md.setData(LineEditForDateTimeEdit.MIME_TYPE, self.dateTime().toString(Qt.ISODate).encode('ascii'))
|
||||
QApplication.instance().clipboard().setMimeData(md)
|
||||
|
||||
def date_time_pasted(self, qt_dt):
|
||||
self.setDateTime(qt_dt)
|
||||
|
||||
def create_context_menu(self):
|
||||
m = QMenu(self)
|
||||
m.addAction(_('Set date to undefined') + '\t' + QKeySequence(Qt.Key_Minus).toString(QKeySequence.NativeText),
|
||||
self.clear_date)
|
||||
m.addAction(_('Set date to today') + '\t' + QKeySequence(Qt.Key_Equal).toString(QKeySequence.NativeText),
|
||||
self.today_date)
|
||||
m.addSeparator()
|
||||
populate_standard_spinbox_context_menu(self, m)
|
||||
return m
|
||||
|
||||
def contextMenuEvent(self, ev):
|
||||
m = self.create_context_menu()
|
||||
m.popup(ev.globalPos())
|
||||
|
||||
def today_date(self):
|
||||
self.setDateTime(QDateTime.currentDateTime())
|
||||
|
||||
def clear_date(self):
|
||||
self.setDateTime(UNDEFINED_QDATETIME)
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
if ev.key() == Qt.Key_Minus:
|
||||
ev.accept()
|
||||
self.clear_date()
|
||||
elif ev.key() == Qt.Key_Equal:
|
||||
self.today_date()
|
||||
ev.accept()
|
||||
elif ev.matches(QKeySequence.Copy):
|
||||
self.lineEdit().copy()
|
||||
ev.accept()
|
||||
elif ev.matches(QKeySequence.Paste):
|
||||
self.lineEdit().paste()
|
||||
ev.accept()
|
||||
else:
|
||||
return QDateTimeEdit.keyPressEvent(self, ev)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from calibre.gui2 import Application
|
||||
app = Application([])
|
||||
|
Loading…
x
Reference in New Issue
Block a user