diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index ff30fff2c1..56dbe4fab6 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -124,17 +124,22 @@ class CopyButton(QPushButton): def keyPressEvent(self, ev): - if ev.key() in self.ACTION_KEYS: - self.copied() - else: - QPushButton.event(self, ev) + try: + if ev.key() in self.ACTION_KEYS: + self.copied() + return + except: + pass + return QPushButton.event(self, ev) def keyReleaseEvent(self, ev): - if ev.key() in self.ACTION_KEYS: + try: + if ev.key() in self.ACTION_KEYS: + return + except: pass - else: - QPushButton.event(self, ev) + return QPushButton.event(self, ev) def mouseReleaseEvent(self, ev): ev.accept() diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index efb780a417..b33bf3d619 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -30,7 +30,7 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \ max_available_height, config, info_dialog, \ available_width, GetMetadata from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror -from calibre.gui2.widgets import ProgressIndicator +from calibre.gui2.widgets import ProgressIndicator, IMAGE_EXTENSIONS from calibre.gui2.wizard import move_library from calibre.gui2.dialogs.scheduler import Scheduler from calibre.gui2.update import CheckForUpdates @@ -220,6 +220,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.status_bar.job_done, Qt.QueuedConnection) QObject.connect(self.status_bar, SIGNAL('show_book_info()'), self.show_book_info) + QObject.connect(self.status_bar, SIGNAL('files_dropped(PyQt_PyObject,PyQt_PyObject)'), + self.files_dropped_on_book) ####################### Setup Toolbar ##################### md = QMenu() md.addAction(_('Edit metadata individually')) @@ -826,6 +828,31 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): to_device = self.stack.currentIndex() != 0 self._add_books(paths, to_device) + def files_dropped_on_book(self, event, paths): + accept = False + if self.current_view() is not self.library_view: + return + db = self.library_view.model().db + current_idx = self.library_view.currentIndex() + if not current_idx.isValid(): return + cid = db.id(current_idx.row()) + for path in paths: + ext = os.path.splitext(path)[1].lower() + if ext: + ext = ext[1:] + if ext in IMAGE_EXTENSIONS: + pmap = QPixmap() + pmap.load(path) + if not pmap.isNull(): + accept = True + db.set_cover(cid, pmap) + elif ext in BOOK_EXTENSIONS: + db.add_format_with_hooks(cid, ext, path, index_is_id=True) + accept = True + if accept: + event.accept() + self.cover_cache.refresh([cid]) + self.library_view.model().current_changed(current_idx, current_idx) def add_filesystem_book(self, path): if os.access(path, os.R_OK): diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py index 86fe117768..6cd8e83db4 100644 --- a/src/calibre/gui2/status.py +++ b/src/calibre/gui2/status.py @@ -1,14 +1,48 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -import re, collections +import os, re, collections from PyQt4.QtGui import QStatusBar, QMovie, QLabel, QWidget, QHBoxLayout, QPixmap, \ QVBoxLayout, QSizePolicy, QToolButton, QIcon, QScrollArea, QFrame from PyQt4.QtCore import Qt, QSize, SIGNAL, QCoreApplication from calibre import fit_image, preferred_encoding, isosx from calibre.gui2 import qstring_to_unicode, config +from calibre.gui2.widgets import IMAGE_EXTENSIONS +from calibre.ebooks import BOOK_EXTENSIONS class BookInfoDisplay(QWidget): + + DROPABBLE_EXTENSIONS = IMAGE_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('files_dropped(PyQt_PyObject, PyQt_PyObject)'), event, + paths) + + def dragMoveEvent(self, event): + event.acceptProposedAction() + + class BookCoverDisplay(QLabel): WIDTH = 81 @@ -184,15 +218,22 @@ class StatusBar(QStatusBar): self.addPermanentWidget(self.tag_view_button) self.addPermanentWidget(self.movie_button) self.book_info = BookInfoDisplay(self.clearMessage) + self.book_info.setAcceptDrops(True) self.scroll_area = QScrollArea() self.scroll_area.setWidget(self.book_info) self.scroll_area.setMaximumHeight(120) self.scroll_area.setWidgetResizable(True) self.connect(self.book_info, SIGNAL('show_book_info()'), self.show_book_info) + self.connect(self.book_info, + SIGNAL('files_dropped(PyQt_PyObject,PyQt_PyObject)'), + self.files_dropped, Qt.QueuedConnection) self.addWidget(self.scroll_area, 100) self.setMinimumHeight(120) self.setMaximumHeight(120) + def files_dropped(self, event, paths): + self.emit(SIGNAL('files_dropped(PyQt_PyObject, PyQt_PyObject)'), event, + paths) def reset_info(self): self.book_info.show_data({}) @@ -243,7 +284,6 @@ if __name__ == '__main__': # Used to create the animated status icon from PyQt4.Qt import QApplication, QPainter, QSvgRenderer, QColor from subprocess import check_call - import os app = QApplication([]) def create_pixmaps(path, size=16, delta=20): diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index c0f8b13e44..e1c9aaf9c3 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -105,13 +105,13 @@ class FilenamePattern(QWidget, Ui_Form): return pat - +IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'gif', 'png', 'bmp'] class ImageView(QLabel): MAX_WIDTH = 400 MAX_HEIGHT = 300 - DROPABBLE_EXTENSIONS = ('jpg', 'jpeg', 'gif', 'png', 'bmp') + DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS @classmethod def paths_from_event(cls, event):