Implement #2931 (Allow drag and drop adding of new formats)

This commit is contained in:
Kovid Goyal 2009-10-08 15:45:03 -06:00
parent 6c3dca52c8
commit 21b8b2a68a
3 changed files with 101 additions and 10 deletions

View File

@ -12,8 +12,8 @@ import time
import traceback import traceback
from datetime import datetime, timedelta from datetime import datetime, timedelta
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread, QDate from PyQt4.Qt import SIGNAL, QObject, QCoreApplication, Qt, QTimer, QThread, QDate, \
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog QPixmap, QListWidgetItem, QDialog, QListWidget
from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \ from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \
choose_files, choose_images, ResizableDialog choose_files, choose_images, ResizableDialog
@ -80,6 +80,37 @@ class Format(QListWidgetItem):
QListWidgetItem.__init__(self, file_icon_provider().icon_from_ext(ext), QListWidgetItem.__init__(self, file_icon_provider().icon_from_ext(ext),
text, parent, QListWidgetItem.UserType) 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): class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
COVER_FETCH_TIMEOUT = 240 # seconds COVER_FETCH_TIMEOUT = 240 # seconds
@ -129,16 +160,21 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
def add_format(self, x): def add_format(self, x):
files = choose_files(self, 'add formats dialog', files = choose_files(self, 'add formats dialog',
"Choose formats for " + qstring_to_unicode((self.title.text())), _("Choose formats for ") + unicode((self.title.text())),
[('Books', BOOK_EXTENSIONS)]) [(_('Books'), BOOK_EXTENSIONS)])
if not files: self._add_formats(files)
return
for _file in files: def _add_formats(self, paths):
added = False
if not paths:
return added
bad_perms = []
for _file in paths:
_file = os.path.abspath(_file) _file = os.path.abspath(_file)
if not os.access(_file, os.R_OK): if not os.access(_file, os.R_OK):
QErrorMessage(self.window).showMessage("You do not have "+\ bad_perms.append(_file)
"permission to read the file: " + _file)
continue continue
_file = run_plugins_on_import(_file) _file = run_plugins_on_import(_file)
size = os.stat(_file).st_size size = os.stat(_file).st_size
ext = os.path.splitext(_file)[1].lower().replace('.', '') ext = os.path.splitext(_file)[1].lower().replace('.', '')
@ -149,6 +185,17 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
break break
Format(self.formats, ext, size, path=_file) Format(self.formats, ext, size, path=_file)
self.formats_changed = True 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): def remove_format(self, x):
rows = self.formats.selectionModel().selectedRows(0) rows = self.formats.selectionModel().selectedRows(0)
@ -276,6 +323,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.row = row self.row = row
self.cover_data = None self.cover_data = None
self.formats_changed = False self.formats_changed = False
self.formats.setAcceptDrops(True)
self.cover_changed = False self.cover_changed = False
self.cpixmap = None self.cpixmap = None
self.cover.setAcceptDrops(True) self.cover.setAcceptDrops(True)
@ -287,6 +335,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.select_cover) self.select_cover)
QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), \ QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), \
self.add_format) 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)"), \ QObject.connect(self.remove_format_button, SIGNAL("clicked(bool)"), \
self.remove_format) self.remove_format)
QObject.connect(self.fetch_metadata_button, SIGNAL('clicked()'), QObject.connect(self.fetch_metadata_button, SIGNAL('clicked()'),

View File

@ -439,7 +439,7 @@
<item> <item>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="1" rowspan="3"> <item row="0" column="1" rowspan="3">
<widget class="QListWidget" name="formats"> <widget class="FormatList" name="formats">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -452,6 +452,9 @@
<height>130</height> <height>130</height>
</size> </size>
</property> </property>
<property name="dragDropMode">
<enum>QAbstractItemView::DropOnly</enum>
</property>
<property name="iconSize"> <property name="iconSize">
<size> <size>
<width>64</width> <width>64</width>
@ -703,6 +706,11 @@
<extends>QLineEdit</extends> <extends>QLineEdit</extends>
<header>widgets.h</header> <header>widgets.h</header>
</customwidget> </customwidget>
<customwidget>
<class>FormatList</class>
<extends>QListWidget</extends>
<header location="global">calibre/gui2/widgets.h</header>
</customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>title</tabstop> <tabstop>title</tabstop>

View File

@ -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.gui2.filename_pattern_ui import Ui_Form
from calibre import fit_image from calibre import fit_image
from calibre.utils.fonts import fontconfig from calibre.utils.fonts import fontconfig
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.metadata.meta import metadata_from_filename from calibre.ebooks.metadata.meta import metadata_from_filename
from calibre.utils.config import prefs from calibre.utils.config import prefs
@ -107,6 +108,37 @@ class FilenamePattern(QWidget, Ui_Form):
IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png', 'bmp'] 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): class ImageView(QLabel):
MAX_WIDTH = 400 MAX_WIDTH = 400