mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
Add two features: 1) The ability to update cached metadata on the device by right clicking the device icon and selecting 'Update cached metadata'. This has the same effect as disconnecting and re-connecting the device. It basically refreshes the device view to reflect any changes made to metadata in the library after the device was connected. 2) Allow manually matching books on the device to book in the library. You can right click on a book in the device view and select "Match book to library" to tell calibre that a book on the device is the same as a particular book in the library. For the change to stick, you have to use the 'Update cached metadata' action described above.
This commit is contained in:
commit
b18b6e98b9
2
.gitignore
vendored
2
.gitignore
vendored
@ -14,7 +14,6 @@ build
|
|||||||
dist
|
dist
|
||||||
docs
|
docs
|
||||||
resources/localization
|
resources/localization
|
||||||
resources/images.qrc
|
|
||||||
resources/scripts.pickle
|
resources/scripts.pickle
|
||||||
resources/ebook-convert-complete.pickle
|
resources/ebook-convert-complete.pickle
|
||||||
resources/builtin_recipes.xml
|
resources/builtin_recipes.xml
|
||||||
@ -42,3 +41,4 @@ calibre_plugins/
|
|||||||
recipes/*.mobi
|
recipes/*.mobi
|
||||||
recipes/*.epub
|
recipes/*.epub
|
||||||
recipes/debug
|
recipes/debug
|
||||||
|
/.metadata/
|
||||||
|
@ -886,6 +886,11 @@ class ActionEditCollections(InterfaceActionBase):
|
|||||||
actual_plugin = 'calibre.gui2.actions.edit_collections:EditCollectionsAction'
|
actual_plugin = 'calibre.gui2.actions.edit_collections:EditCollectionsAction'
|
||||||
description = _('Edit the collections in which books are placed on your device')
|
description = _('Edit the collections in which books are placed on your device')
|
||||||
|
|
||||||
|
class ActionMatchBooks(InterfaceActionBase):
|
||||||
|
name = 'Match Books'
|
||||||
|
actual_plugin = 'calibre.gui2.actions.match_books:MatchBookAction'
|
||||||
|
description = _('Match book on the devices to books in the library')
|
||||||
|
|
||||||
class ActionCopyToLibrary(InterfaceActionBase):
|
class ActionCopyToLibrary(InterfaceActionBase):
|
||||||
name = 'Copy To Library'
|
name = 'Copy To Library'
|
||||||
actual_plugin = 'calibre.gui2.actions.copy_to_library:CopyToLibraryAction'
|
actual_plugin = 'calibre.gui2.actions.copy_to_library:CopyToLibraryAction'
|
||||||
@ -936,7 +941,7 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
|
|||||||
ActionFetchNews, ActionSaveToDisk, ActionQuickview, ActionPolish,
|
ActionFetchNews, ActionSaveToDisk, ActionQuickview, ActionPolish,
|
||||||
ActionShowBookDetails,ActionRestart, ActionOpenFolder, ActionConnectShare,
|
ActionShowBookDetails,ActionRestart, ActionOpenFolder, ActionConnectShare,
|
||||||
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
|
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
|
||||||
ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary,
|
ActionAddToLibrary, ActionEditCollections, ActionMatchBooks, ActionChooseLibrary,
|
||||||
ActionCopyToLibrary, ActionTweakEpub, ActionNextMatch, ActionStore,
|
ActionCopyToLibrary, ActionTweakEpub, ActionNextMatch, ActionStore,
|
||||||
ActionPluginUpdater, ActionPickRandom, ActionEditToC]
|
ActionPluginUpdater, ActionPickRandom, ActionEditToC]
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ defs['action-layout-context-menu'] = (
|
|||||||
|
|
||||||
defs['action-layout-context-menu-device'] = (
|
defs['action-layout-context-menu-device'] = (
|
||||||
'View', 'Save To Disk', None, 'Remove Books', None,
|
'View', 'Save To Disk', None, 'Remove Books', None,
|
||||||
'Add To Library', 'Edit Collections',
|
'Add To Library', 'Edit Collections', 'Match Books'
|
||||||
)
|
)
|
||||||
|
|
||||||
defs['action-layout-context-menu-cover-browser'] = (
|
defs['action-layout-context-menu-cover-browser'] = (
|
||||||
|
39
src/calibre/gui2/actions/match_books.py
Normal file
39
src/calibre/gui2/actions/match_books.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from calibre.gui2 import error_dialog
|
||||||
|
from calibre.gui2.actions import InterfaceAction
|
||||||
|
from calibre.gui2.dialogs.match_books import MatchBooks
|
||||||
|
|
||||||
|
class MatchBookAction(InterfaceAction):
|
||||||
|
|
||||||
|
name = 'Match Books'
|
||||||
|
action_spec = (_('Match book to library'), 'book.png',
|
||||||
|
_('Match this book to a book in the library'),
|
||||||
|
())
|
||||||
|
dont_add_to = frozenset(['menubar', 'toolbar', 'context-menu', 'toolbar-child'])
|
||||||
|
action_type = 'current'
|
||||||
|
|
||||||
|
def genesis(self):
|
||||||
|
self.qaction.triggered.connect(self.match_books_in_library)
|
||||||
|
|
||||||
|
def location_selected(self, loc):
|
||||||
|
enabled = loc != 'library'
|
||||||
|
self.qaction.setEnabled(enabled)
|
||||||
|
|
||||||
|
def match_books_in_library(self, *args):
|
||||||
|
view = self.gui.current_view()
|
||||||
|
rows = view.selectionModel().selectedRows()
|
||||||
|
if not rows or len(rows) != 1:
|
||||||
|
d = error_dialog(self.gui, _('Match books'), _('You must select one book'))
|
||||||
|
d.exec_()
|
||||||
|
return
|
||||||
|
|
||||||
|
id_ = view.model().indices(rows)[0]
|
||||||
|
MatchBooks(self.gui, view, id_).exec_()
|
@ -1604,6 +1604,10 @@ class DeviceMixin(object): # {{{
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def update_metadata_on_device(self):
|
||||||
|
self.set_books_in_library(self.booklists(), reset=True, force_send=True)
|
||||||
|
self.refresh_ondevice()
|
||||||
|
|
||||||
def book_on_device(self, id, reset=False):
|
def book_on_device(self, id, reset=False):
|
||||||
'''
|
'''
|
||||||
Return an indication of whether the given book represented by its db id
|
Return an indication of whether the given book represented by its db id
|
||||||
@ -1652,7 +1656,8 @@ class DeviceMixin(object): # {{{
|
|||||||
loc[4] |= self.book_db_uuid_path_map[id]
|
loc[4] |= self.book_db_uuid_path_map[id]
|
||||||
return loc
|
return loc
|
||||||
|
|
||||||
def set_books_in_library(self, booklists, reset=False, add_as_step_to_job=None):
|
def set_books_in_library(self, booklists, reset=False, add_as_step_to_job=None,
|
||||||
|
force_send=False):
|
||||||
'''
|
'''
|
||||||
Set the ondevice indications in the device database.
|
Set the ondevice indications in the device database.
|
||||||
This method should be called before book_on_device is called, because
|
This method should be called before book_on_device is called, because
|
||||||
@ -1675,7 +1680,8 @@ class DeviceMixin(object): # {{{
|
|||||||
x = x.lower() if x else ''
|
x = x.lower() if x else ''
|
||||||
return string_pat.sub('', x)
|
return string_pat.sub('', x)
|
||||||
|
|
||||||
update_metadata = device_prefs['manage_device_metadata'] == 'on_connect'
|
update_metadata = (
|
||||||
|
device_prefs['manage_device_metadata'] == 'on_connect' or force_send)
|
||||||
|
|
||||||
get_covers = False
|
get_covers = False
|
||||||
desired_thumbnail_height = 0
|
desired_thumbnail_height = 0
|
||||||
|
202
src/calibre/gui2/dialogs/match_books.py
Normal file
202
src/calibre/gui2/dialogs/match_books.py
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2013, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
from PyQt4.Qt import (Qt, QDialog, QAbstractItemView, QTableWidgetItem,
|
||||||
|
QByteArray)
|
||||||
|
|
||||||
|
from calibre.gui2 import gprefs, error_dialog
|
||||||
|
from calibre.gui2.dialogs.match_books_ui import Ui_MatchBooks
|
||||||
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
|
class TableItem(QTableWidgetItem):
|
||||||
|
'''
|
||||||
|
A QTableWidgetItem that sorts on a separate string and uses ICU rules
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, val, sort, idx=0):
|
||||||
|
self.sort = sort
|
||||||
|
self.sort_idx = idx
|
||||||
|
QTableWidgetItem.__init__(self, val)
|
||||||
|
self.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
|
||||||
|
|
||||||
|
def __ge__(self, other):
|
||||||
|
l = sort_key(self.sort)
|
||||||
|
r = sort_key(other.sort)
|
||||||
|
if l > r:
|
||||||
|
return 1
|
||||||
|
if l == r:
|
||||||
|
return self.sort_idx >= other.sort_idx
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
l = sort_key(self.sort)
|
||||||
|
r = sort_key(other.sort)
|
||||||
|
if l < r:
|
||||||
|
return 1
|
||||||
|
if l == r:
|
||||||
|
return self.sort_idx < other.sort_idx
|
||||||
|
return 0
|
||||||
|
|
||||||
|
class MatchBooks(QDialog, Ui_MatchBooks):
|
||||||
|
|
||||||
|
def __init__(self, gui, view, id_):
|
||||||
|
QDialog.__init__(self, gui, flags=Qt.Window)
|
||||||
|
Ui_MatchBooks.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.isClosed = False
|
||||||
|
|
||||||
|
self.books_table_column_widths = None
|
||||||
|
try:
|
||||||
|
self.books_table_column_widths = \
|
||||||
|
gprefs.get('match_books_dialog_books_table_widths', None)
|
||||||
|
geom = gprefs.get('match_books_dialog_geometry', bytearray(''))
|
||||||
|
self.restoreGeometry(QByteArray(geom))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.search_text.initialize('match_books_dialog')
|
||||||
|
|
||||||
|
# Remove the help button from the window title bar
|
||||||
|
icon = self.windowIcon()
|
||||||
|
self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint))
|
||||||
|
self.setWindowIcon(icon)
|
||||||
|
|
||||||
|
self.device_db = view.model().db
|
||||||
|
self.library_db = gui.library_view.model().db
|
||||||
|
self.view = view
|
||||||
|
self.gui = gui
|
||||||
|
self.current_device_book_id = id_
|
||||||
|
self.current_library_book_id = None
|
||||||
|
|
||||||
|
# Set up the books table columns
|
||||||
|
self.books_table.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
|
self.books_table.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||||
|
self.books_table.setColumnCount(3)
|
||||||
|
t = QTableWidgetItem(_('Title'))
|
||||||
|
self.books_table.setHorizontalHeaderItem(0, t)
|
||||||
|
t = QTableWidgetItem(_('Authors'))
|
||||||
|
self.books_table.setHorizontalHeaderItem(1, t)
|
||||||
|
t = QTableWidgetItem(_('Series'))
|
||||||
|
self.books_table.setHorizontalHeaderItem(2, t)
|
||||||
|
self.books_table_header_height = self.books_table.height()
|
||||||
|
self.books_table.cellDoubleClicked.connect(self.book_doubleclicked)
|
||||||
|
self.books_table.cellClicked.connect(self.book_clicked)
|
||||||
|
self.books_table.sortByColumn(0, Qt.AscendingOrder)
|
||||||
|
|
||||||
|
# get the standard table row height. Do this here because calling
|
||||||
|
# resizeRowsToContents can word wrap long cell contents, creating
|
||||||
|
# double-high rows
|
||||||
|
self.books_table.setRowCount(1)
|
||||||
|
self.books_table.setItem(0, 0, TableItem('A', ''))
|
||||||
|
self.books_table.resizeRowsToContents()
|
||||||
|
self.books_table_row_height = self.books_table.rowHeight(0)
|
||||||
|
self.books_table.setRowCount(0)
|
||||||
|
|
||||||
|
self.search_button.clicked.connect(self.do_search)
|
||||||
|
self.search_button.setDefault(False)
|
||||||
|
self.search_text.lineEdit().returnPressed.connect(self.return_pressed)
|
||||||
|
|
||||||
|
self.buttonBox.accepted.connect(self.accept)
|
||||||
|
self.buttonBox.rejected.connect(self.reject)
|
||||||
|
self.ignore_next_key = False
|
||||||
|
|
||||||
|
def return_pressed(self):
|
||||||
|
self.ignore_next_key = True
|
||||||
|
self.do_search()
|
||||||
|
|
||||||
|
def keyPressEvent(self, e):
|
||||||
|
if self.ignore_next_key:
|
||||||
|
self.ignore_next_key = False
|
||||||
|
else:
|
||||||
|
QDialog.keyPressEvent(self, e)
|
||||||
|
|
||||||
|
def do_search(self):
|
||||||
|
query = unicode(self.search_text.text())
|
||||||
|
if not query:
|
||||||
|
d = error_dialog(self.gui, _('Match books'),
|
||||||
|
_('You must enter a search expression into the search box'))
|
||||||
|
d.exec_()
|
||||||
|
return
|
||||||
|
books = self.library_db.data.search(query, return_matches=True)
|
||||||
|
self.books_table.setRowCount(len(books))
|
||||||
|
|
||||||
|
self.books_table.setSortingEnabled(False)
|
||||||
|
for row, b in enumerate(books):
|
||||||
|
mi = self.library_db.get_metadata(b, index_is_id=True, get_user_categories=False)
|
||||||
|
a = TableItem(mi.title, mi.title_sort)
|
||||||
|
a.setData(Qt.UserRole, b)
|
||||||
|
self.books_table.setItem(row, 0, a)
|
||||||
|
a = TableItem(' & '.join(mi.authors), mi.author_sort)
|
||||||
|
self.books_table.setItem(row, 1, a)
|
||||||
|
series = mi.format_field('series')[1]
|
||||||
|
if series is None:
|
||||||
|
series = ''
|
||||||
|
a = TableItem(series, mi.series, mi.series_index)
|
||||||
|
self.books_table.setItem(row, 2, a)
|
||||||
|
self.books_table.setRowHeight(row, self.books_table_row_height)
|
||||||
|
|
||||||
|
self.books_table.setSortingEnabled(True)
|
||||||
|
|
||||||
|
# Deal with sizing the table columns. Done here because the numbers are not
|
||||||
|
# correct until the first paint.
|
||||||
|
def resizeEvent(self, *args):
|
||||||
|
QDialog.resizeEvent(self, *args)
|
||||||
|
if self.books_table_column_widths is not None:
|
||||||
|
for c,w in enumerate(self.books_table_column_widths):
|
||||||
|
self.books_table.setColumnWidth(c, w)
|
||||||
|
else:
|
||||||
|
# the vertical scroll bar might not be rendered, so might not yet
|
||||||
|
# have a width. Assume 25. Not a problem because user-changed column
|
||||||
|
# widths will be remembered
|
||||||
|
w = self.books_table.width() - 25 - self.books_table.verticalHeader().width()
|
||||||
|
w /= self.books_table.columnCount()
|
||||||
|
for c in range(0, self.books_table.columnCount()):
|
||||||
|
self.books_table.setColumnWidth(c, w)
|
||||||
|
self.save_state()
|
||||||
|
|
||||||
|
def book_clicked(self, row, column):
|
||||||
|
self.book_selected = True
|
||||||
|
id_ = self.books_table.item(row, 0).data(Qt.UserRole).toInt()[0]
|
||||||
|
self.current_library_book_id = id_
|
||||||
|
|
||||||
|
def book_doubleclicked(self, row, column):
|
||||||
|
self.book_clicked(row, column)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def save_state(self):
|
||||||
|
self.books_table_column_widths = []
|
||||||
|
for c in range(0, self.books_table.columnCount()):
|
||||||
|
self.books_table_column_widths.append(self.books_table.columnWidth(c))
|
||||||
|
gprefs['match_books_dialog_books_table_widths'] = self.books_table_column_widths
|
||||||
|
gprefs['match_books_dialog_geometry'] = bytearray(self.saveGeometry())
|
||||||
|
self.search_text.save_history()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.save_state()
|
||||||
|
# clean up to prevent memory leaks
|
||||||
|
self.device_db = self.view = self.gui = None
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
if not self.current_library_book_id:
|
||||||
|
d = error_dialog(self.gui, _('Match books'),
|
||||||
|
_('You must select a matching book'))
|
||||||
|
d.exec_()
|
||||||
|
return
|
||||||
|
mi = self.library_db.get_metadata(self.current_library_book_id,
|
||||||
|
index_is_id=True, get_user_categories=False)
|
||||||
|
self.device_db[self.current_device_book_id].smart_update(mi, replace_metadata=True)
|
||||||
|
self.device_db[self.current_device_book_id].in_library_waiting = True
|
||||||
|
self.save_state()
|
||||||
|
QDialog.accept(self)
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
self.close()
|
||||||
|
QDialog.reject(self)
|
||||||
|
|
140
src/calibre/gui2/dialogs/match_books.ui
Normal file
140
src/calibre/gui2/dialogs/match_books.ui
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MatchBooks</class>
|
||||||
|
<widget class="QDialog" name="MatchBooks">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>751</width>
|
||||||
|
<height>342</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Match Books</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="HistoryLineEdit" name="search_text">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>100</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>350</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="sizeAdjustPolicy">
|
||||||
|
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||||
|
</property>
|
||||||
|
<property name="minimumContentsLength">
|
||||||
|
<number>30</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="search_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Search</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Do a search to find the book you want to match</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QTableWidget" name="books_table">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>4</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="rowCount">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="columnCount">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>Remember to update metadata on the device when you are done (Right click the device icon and select <i>Update cached metadata</i>)</p></string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<layout class="QHBoxLayout">
|
||||||
|
<item>
|
||||||
|
<spacer>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
<property name="centerButtons">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>HistoryLineEdit</class>
|
||||||
|
<extends>QComboBox</extends>
|
||||||
|
<header>calibre/gui2/widgets.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>MatchBooks</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>297</x>
|
||||||
|
<y>217</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>234</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -25,6 +25,7 @@ class LocationManager(QObject): # {{{
|
|||||||
unmount_device = pyqtSignal()
|
unmount_device = pyqtSignal()
|
||||||
location_selected = pyqtSignal(object)
|
location_selected = pyqtSignal(object)
|
||||||
configure_device = pyqtSignal()
|
configure_device = pyqtSignal()
|
||||||
|
update_device_metadata = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QObject.__init__(self, parent)
|
QObject.__init__(self, parent)
|
||||||
@ -60,6 +61,9 @@ class LocationManager(QObject): # {{{
|
|||||||
a = m.addAction(QIcon(I('config.png')), _('Configure this device'))
|
a = m.addAction(QIcon(I('config.png')), _('Configure this device'))
|
||||||
a.triggered.connect(self._configure_requested)
|
a.triggered.connect(self._configure_requested)
|
||||||
self._mem.append(a)
|
self._mem.append(a)
|
||||||
|
a = m.addAction(QIcon(I('sync.png')), _('Update cached metadata on device'))
|
||||||
|
a.triggered.connect(lambda x : self.update_device_metadata.emit())
|
||||||
|
self._mem.append(a)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ac.setToolTip(tooltip)
|
ac.setToolTip(tooltip)
|
||||||
|
@ -1207,6 +1207,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
self.search_engine = OnDeviceSearch(self)
|
self.search_engine = OnDeviceSearch(self)
|
||||||
self.editable = ['title', 'authors', 'collections']
|
self.editable = ['title', 'authors', 'collections']
|
||||||
self.book_in_library = None
|
self.book_in_library = None
|
||||||
|
self.sync_icon = QIcon(I('sync.png'))
|
||||||
|
|
||||||
|
|
||||||
def counts(self):
|
def counts(self):
|
||||||
return Counts(len(self.db), len(self.db), len(self.map))
|
return Counts(len(self.db), len(self.db), len(self.map))
|
||||||
@ -1535,6 +1537,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
elif DEBUG and cname == 'inlibrary':
|
elif DEBUG and cname == 'inlibrary':
|
||||||
return QVariant(self.db[self.map[row]].in_library)
|
return QVariant(self.db[self.map[row]].in_library)
|
||||||
elif role == Qt.ToolTipRole and index.isValid():
|
elif role == Qt.ToolTipRole and index.isValid():
|
||||||
|
if col == 0 and hasattr(self.db[self.map[row]], 'in_library_waiting'):
|
||||||
|
return QVariant(_('Waiting for metadata to be updated'))
|
||||||
if self.is_row_marked_for_deletion(row):
|
if self.is_row_marked_for_deletion(row):
|
||||||
return QVariant(_('Marked for deletion'))
|
return QVariant(_('Marked for deletion'))
|
||||||
if cname in ['title', 'authors'] or (cname == 'collections' and
|
if cname in ['title', 'authors'] or (cname == 'collections' and
|
||||||
@ -1543,6 +1547,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
elif role == Qt.DecorationRole and cname == 'inlibrary':
|
elif role == Qt.DecorationRole and cname == 'inlibrary':
|
||||||
if self.db[self.map[row]].in_library:
|
if self.db[self.map[row]].in_library:
|
||||||
return QVariant(self.bool_yes_icon)
|
return QVariant(self.bool_yes_icon)
|
||||||
|
elif hasattr(self.db[self.map[row]], 'in_library_waiting'):
|
||||||
|
return QVariant(self.sync_icon)
|
||||||
elif self.db[self.map[row]].in_library is not None:
|
elif self.db[self.map[row]].in_library is not None:
|
||||||
return QVariant(self.bool_no_icon)
|
return QVariant(self.bool_no_icon)
|
||||||
elif role == Qt.TextAlignmentRole:
|
elif role == Qt.TextAlignmentRole:
|
||||||
|
@ -293,6 +293,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.location_manager.location_selected.connect(self.location_selected)
|
self.location_manager.location_selected.connect(self.location_selected)
|
||||||
self.location_manager.unmount_device.connect(self.device_manager.umount_device)
|
self.location_manager.unmount_device.connect(self.device_manager.umount_device)
|
||||||
self.location_manager.configure_device.connect(self.configure_connected_device)
|
self.location_manager.configure_device.connect(self.configure_connected_device)
|
||||||
|
self.location_manager.update_device_metadata.connect(self.update_metadata_on_device)
|
||||||
self.eject_action.triggered.connect(self.device_manager.umount_device)
|
self.eject_action.triggered.connect(self.device_manager.umount_device)
|
||||||
|
|
||||||
#################### Update notification ###################
|
#################### Update notification ###################
|
||||||
|
Loading…
x
Reference in New Issue
Block a user