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
|
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()'),
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user