mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement #2931 (Allow drag and drop adding of new formats)
This commit is contained in:
parent
6c3dca52c8
commit
21b8b2a68a
@ -12,8 +12,8 @@ import time
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread, QDate
|
||||
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog
|
||||
from PyQt4.Qt import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread, QDate, \
|
||||
QPixmap, QListWidgetItem, QDialog, QListWidget
|
||||
|
||||
from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \
|
||||
choose_files, choose_images, ResizableDialog
|
||||
@ -80,6 +80,37 @@ class Format(QListWidgetItem):
|
||||
QListWidgetItem.__init__(self, file_icon_provider().icon_from_ext(ext),
|
||||
text, parent, QListWidgetItem.UserType)
|
||||
|
||||
class FormatList(QListWidget):
|
||||
DROPABBLE_EXTENSIONS = BOOK_EXTENSIONS
|
||||
|
||||
@classmethod
|
||||
def paths_from_event(cls, event):
|
||||
'''
|
||||
Accept a drop event and return a list of paths that can be read from
|
||||
and represent files with extensions.
|
||||
'''
|
||||
if event.mimeData().hasFormat('text/uri-list'):
|
||||
urls = [unicode(u.toLocalFile()) for u in event.mimeData().urls()]
|
||||
urls = [u for u in urls if os.path.splitext(u)[1] and os.access(u, os.R_OK)]
|
||||
return [u for u in urls if os.path.splitext(u)[1][1:].lower() in cls.DROPABBLE_EXTENSIONS]
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if int(event.possibleActions() & Qt.CopyAction) + \
|
||||
int(event.possibleActions() & Qt.MoveAction) == 0:
|
||||
return
|
||||
paths = self.paths_from_event(event)
|
||||
if paths:
|
||||
event.acceptProposedAction()
|
||||
|
||||
def dropEvent(self, event):
|
||||
paths = self.paths_from_event(event)
|
||||
event.setDropAction(Qt.CopyAction)
|
||||
self.emit(SIGNAL('formats_dropped(PyQt_PyObject,PyQt_PyObject)'),
|
||||
event, paths)
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
event.acceptProposedAction()
|
||||
|
||||
class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
|
||||
COVER_FETCH_TIMEOUT = 240 # seconds
|
||||
@ -129,16 +160,21 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
|
||||
def add_format(self, x):
|
||||
files = choose_files(self, 'add formats dialog',
|
||||
"Choose formats for " + qstring_to_unicode((self.title.text())),
|
||||
[('Books', BOOK_EXTENSIONS)])
|
||||
if not files:
|
||||
return
|
||||
for _file in files:
|
||||
_("Choose formats for ") + unicode((self.title.text())),
|
||||
[(_('Books'), BOOK_EXTENSIONS)])
|
||||
self._add_formats(files)
|
||||
|
||||
def _add_formats(self, paths):
|
||||
added = False
|
||||
if not paths:
|
||||
return added
|
||||
bad_perms = []
|
||||
for _file in paths:
|
||||
_file = os.path.abspath(_file)
|
||||
if not os.access(_file, os.R_OK):
|
||||
QErrorMessage(self.window).showMessage("You do not have "+\
|
||||
"permission to read the file: " + _file)
|
||||
bad_perms.append(_file)
|
||||
continue
|
||||
|
||||
_file = run_plugins_on_import(_file)
|
||||
size = os.stat(_file).st_size
|
||||
ext = os.path.splitext(_file)[1].lower().replace('.', '')
|
||||
@ -149,6 +185,17 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
break
|
||||
Format(self.formats, ext, size, path=_file)
|
||||
self.formats_changed = True
|
||||
added = True
|
||||
if bad_perms:
|
||||
error_dialog(self.window, _('You do not have '
|
||||
'permission to read the following files:'),
|
||||
det_msg='\n'.join(bad_perms), show=True)
|
||||
|
||||
return added
|
||||
|
||||
def formats_dropped(self, event, paths):
|
||||
if self._add_formats(paths):
|
||||
event.accept()
|
||||
|
||||
def remove_format(self, x):
|
||||
rows = self.formats.selectionModel().selectedRows(0)
|
||||
@ -276,6 +323,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
self.row = row
|
||||
self.cover_data = None
|
||||
self.formats_changed = False
|
||||
self.formats.setAcceptDrops(True)
|
||||
self.cover_changed = False
|
||||
self.cpixmap = None
|
||||
self.cover.setAcceptDrops(True)
|
||||
@ -287,6 +335,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
self.select_cover)
|
||||
QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), \
|
||||
self.add_format)
|
||||
self.connect(self.formats,
|
||||
SIGNAL('formats_dropped(PyQt_PyObject,PyQt_PyObject)'),
|
||||
self.formats_dropped)
|
||||
QObject.connect(self.remove_format_button, SIGNAL("clicked(bool)"), \
|
||||
self.remove_format)
|
||||
QObject.connect(self.fetch_metadata_button, SIGNAL('clicked()'),
|
||||
|
@ -439,7 +439,7 @@
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1" rowspan="3">
|
||||
<widget class="QListWidget" name="formats">
|
||||
<widget class="FormatList" name="formats">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
@ -452,6 +452,9 @@
|
||||
<height>130</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DropOnly</enum>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
@ -703,6 +706,11 @@
|
||||
<extends>QLineEdit</extends>
|
||||
<header>widgets.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>FormatList</class>
|
||||
<extends>QListWidget</extends>
|
||||
<header location="global">calibre/gui2/widgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>title</tabstop>
|
||||
|
@ -19,6 +19,7 @@ from calibre.gui2.dialogs.job_view_ui import Ui_Dialog
|
||||
from calibre.gui2.filename_pattern_ui import Ui_Form
|
||||
from calibre import fit_image
|
||||
from calibre.utils.fonts import fontconfig
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
from calibre.ebooks.metadata.meta import metadata_from_filename
|
||||
from calibre.utils.config import prefs
|
||||
|
||||
@ -107,6 +108,37 @@ class FilenamePattern(QWidget, Ui_Form):
|
||||
|
||||
IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png', 'bmp']
|
||||
|
||||
class FormatList(QListWidget):
|
||||
DROPABBLE_EXTENSIONS = BOOK_EXTENSIONS
|
||||
|
||||
@classmethod
|
||||
def paths_from_event(cls, event):
|
||||
'''
|
||||
Accept a drop event and return a list of paths that can be read from
|
||||
and represent files with extensions.
|
||||
'''
|
||||
if event.mimeData().hasFormat('text/uri-list'):
|
||||
urls = [unicode(u.toLocalFile()) for u in event.mimeData().urls()]
|
||||
urls = [u for u in urls if os.path.splitext(u)[1] and os.access(u, os.R_OK)]
|
||||
return [u for u in urls if os.path.splitext(u)[1][1:].lower() in cls.DROPABBLE_EXTENSIONS]
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if int(event.possibleActions() & Qt.CopyAction) + \
|
||||
int(event.possibleActions() & Qt.MoveAction) == 0:
|
||||
return
|
||||
paths = self.paths_from_event(event)
|
||||
if paths:
|
||||
event.acceptProposedAction()
|
||||
|
||||
def dropEvent(self, event):
|
||||
paths = self.paths_from_event(event)
|
||||
event.setDropAction(Qt.CopyAction)
|
||||
self.emit(SIGNAL('formats_dropped(PyQt_PyObject,PyQt_PyObject)'),
|
||||
event, paths)
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
event.acceptProposedAction()
|
||||
|
||||
class ImageView(QLabel):
|
||||
|
||||
MAX_WIDTH = 400
|
||||
|
Loading…
x
Reference in New Issue
Block a user