mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Commit before merge from trunk
This commit is contained in:
parent
431b9e15bc
commit
a61b71ccb1
@ -25,7 +25,7 @@ NONE = QVariant() #: Null value to return from the data function of item models
|
||||
UNDEFINED_QDATE = QDate(UNDEFINED_DATE)
|
||||
|
||||
ALL_COLUMNS = ['title', 'authors', 'size', 'timestamp', 'rating', 'publisher',
|
||||
'tags', 'series', 'pubdate']
|
||||
'tags', 'series', 'pubdate', 'ondevice']
|
||||
|
||||
def _config():
|
||||
c = Config('gui', 'preferences for the calibre GUI')
|
||||
|
@ -1,7 +1,7 @@
|
||||
from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import os, traceback, Queue, time, socket, cStringIO
|
||||
import os, traceback, Queue, time, socket, cStringIO, re
|
||||
from threading import Thread, RLock
|
||||
from itertools import repeat
|
||||
from functools import partial
|
||||
@ -978,3 +978,55 @@ class DeviceGUI(object):
|
||||
getattr(f, 'close', lambda : True)()
|
||||
if memory and memory[1]:
|
||||
self.library_view.model().delete_books_by_id(memory[1])
|
||||
|
||||
def book_on_device(self, index, index_is_id=False, format=None):
|
||||
loc = [None, None, None]
|
||||
|
||||
db_title = self.library_view.model().db.title(index, index_is_id).lower()
|
||||
db_title = re.sub('(?u)\W|[_]', '', db_title)
|
||||
au = self.library_view.model().db.authors(index, index_is_id)
|
||||
db_authors = au.lower() if au else ''
|
||||
db_authors = re.sub('(?u)\W|[_]', '', db_authors)
|
||||
|
||||
for i, l in enumerate(self.booklists()):
|
||||
for book in l:
|
||||
book_title = book.title.lower() if book.title else ''
|
||||
book_title = re.sub('(?u)\W|[_]', '', book_title)
|
||||
book_authors = authors_to_string(book.authors).lower()
|
||||
book_authors = re.sub('(?u)\W|[_]', '', book_authors)
|
||||
if book_title == db_title and book_authors == db_authors:
|
||||
loc[i] = True
|
||||
break
|
||||
return loc
|
||||
|
||||
def book_in_library(self, index, oncard=None):
|
||||
'''
|
||||
Used to determine if a book on the device is in the library.
|
||||
Returns the book's id in the library.
|
||||
'''
|
||||
bl = []
|
||||
if oncard == 'carda':
|
||||
bl = self.booklists()[1]
|
||||
elif oncard == 'cardb':
|
||||
bl = self.booklists()[2]
|
||||
else:
|
||||
bl = self.booklists()[0]
|
||||
|
||||
book = bl[index]
|
||||
book_title = book.title.lower() if book.title else ''
|
||||
book_title = re.sub('(?u)\W|[_]', '', book_title)
|
||||
book_authors = authors_to_string(book.authors).lower() if book.authors else ''
|
||||
book_authors = re.sub('(?u)\W|[_]', '', book_authors)
|
||||
|
||||
# if getattr(book, 'application_id', None) != None and self.library_view.model().db.has_id(book.application_id):
|
||||
# if book.uuid and self.library_view.model().db.uuid(book.application_id, index_is_id=True) == book.uuid:
|
||||
# return book.application_id
|
||||
for id, title in self.library_view.model().db.all_titles():
|
||||
title = re.sub('(?u)\W|[_]', '', title.lower())
|
||||
if title == book_title:
|
||||
au = self.library_view.model().db.authors(id, index_is_id=True)
|
||||
authors = au.lower() if au else ''
|
||||
authors = re.sub('(?u)\W|[_]', '', authors)
|
||||
if authors == book_authors:
|
||||
return id
|
||||
return None
|
||||
|
@ -316,11 +316,13 @@ class BooksModel(QAbstractTableModel):
|
||||
'publisher' : _("Publisher"),
|
||||
'tags' : _("Tags"),
|
||||
'series' : _("Series"),
|
||||
'ondevice' : _("On Device"),
|
||||
}
|
||||
|
||||
def __init__(self, parent=None, buffer=40):
|
||||
QAbstractTableModel.__init__(self, parent)
|
||||
self.db = None
|
||||
self.book_on_device = None
|
||||
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
||||
'tags', 'series', 'timestamp', 'pubdate']
|
||||
self.default_image = QImage(I('book.svg'))
|
||||
@ -359,6 +361,9 @@ class BooksModel(QAbstractTableModel):
|
||||
self.reset()
|
||||
self.emit(SIGNAL('columns_sorted()'))
|
||||
|
||||
def set_book_on_device_func(self, func):
|
||||
self.book_on_device = func
|
||||
|
||||
def set_database(self, db):
|
||||
self.db = db
|
||||
self.custom_columns = self.db.custom_column_label_map
|
||||
@ -799,6 +804,8 @@ class BooksModel(QAbstractTableModel):
|
||||
'series' : functools.partial(series,
|
||||
idx=self.db.FIELD_MAP['series'],
|
||||
siix=self.db.FIELD_MAP['series_index']),
|
||||
'ondevice' : functools.partial(text_type,
|
||||
idx=self.db.FIELD_MAP['ondevice'], mult=False),
|
||||
}
|
||||
self.dc_decorator = {}
|
||||
|
||||
@ -1255,6 +1262,12 @@ class DeviceBooksModel(BooksModel):
|
||||
self.marked_for_deletion = {}
|
||||
self.search_engine = OnDeviceSearch(self)
|
||||
self.editable = True
|
||||
self.book_in_library = None
|
||||
self.loc = None
|
||||
|
||||
def set_book_in_library_func(self, func, loc):
|
||||
self.book_in_library = func
|
||||
self.loc = loc
|
||||
|
||||
def mark_for_deletion(self, job, rows):
|
||||
self.marked_for_deletion[job] = self.indices(rows)
|
||||
@ -1342,8 +1355,11 @@ class DeviceBooksModel(BooksModel):
|
||||
def tagscmp(x, y):
|
||||
x, y = ','.join(self.db[x].tags), ','.join(self.db[y].tags)
|
||||
return cmp(x, y)
|
||||
def libcmp(x, y):
|
||||
x, y = self.book_in_library(self.map[x], self.loc), self.book_in_library(self.map[y], self.loc)
|
||||
return cmp(x, y)
|
||||
fcmp = strcmp('title_sorter') if col == 0 else strcmp('authors') if col == 1 else \
|
||||
sizecmp if col == 2 else datecmp if col == 3 else tagscmp
|
||||
sizecmp if col == 2 else datecmp if col == 3 else tagscmp if col == 4 else libcmp
|
||||
self.map.sort(cmp=fcmp, reverse=descending)
|
||||
if len(self.map) == len(self.db):
|
||||
self.sorted_map = list(self.map)
|
||||
@ -1357,7 +1373,7 @@ class DeviceBooksModel(BooksModel):
|
||||
def columnCount(self, parent):
|
||||
if parent and parent.isValid():
|
||||
return 0
|
||||
return 5
|
||||
return 6
|
||||
|
||||
def rowCount(self, parent):
|
||||
if parent and parent.isValid():
|
||||
@ -1398,7 +1414,6 @@ class DeviceBooksModel(BooksModel):
|
||||
'''
|
||||
return [ self.map[r.row()] for r in rows]
|
||||
|
||||
|
||||
def data(self, index, role):
|
||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||
row, col = index.row(), index.column()
|
||||
@ -1426,6 +1441,10 @@ class DeviceBooksModel(BooksModel):
|
||||
tags = self.db[self.map[row]].tags
|
||||
if tags:
|
||||
return QVariant(', '.join(tags))
|
||||
elif col == 5:
|
||||
if self.book_in_library:
|
||||
if self.book_in_library(self.map[row], self.loc) != None:
|
||||
return QVariant(_("True"))
|
||||
elif role == Qt.TextAlignmentRole and index.column() in [2, 3]:
|
||||
return QVariant(Qt.AlignRight | Qt.AlignVCenter)
|
||||
elif role == Qt.ToolTipRole and index.isValid():
|
||||
@ -1446,6 +1465,7 @@ class DeviceBooksModel(BooksModel):
|
||||
elif section == 2: text = _("Size (MB)")
|
||||
elif section == 3: text = _("Date")
|
||||
elif section == 4: text = _("Tags")
|
||||
elif section == 5: text = _("In Library")
|
||||
return QVariant(text)
|
||||
else:
|
||||
return QVariant(section+1)
|
||||
@ -1479,4 +1499,3 @@ class DeviceBooksModel(BooksModel):
|
||||
|
||||
def set_search_restriction(self, s):
|
||||
pass
|
||||
|
||||
|
@ -543,7 +543,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
else:
|
||||
self.library_path = dir
|
||||
db = LibraryDatabase2(self.library_path)
|
||||
db.set_book_on_device_func(self.book_on_device)
|
||||
self.library_view.set_database(db)
|
||||
self.library_view.model().set_book_on_device_func(self.book_on_device)
|
||||
prefs['library_path'] = self.library_path
|
||||
self.library_view.restore_sort_at_startup(dynamic.get('sort_history', [('timestamp', Qt.DescendingOrder)]))
|
||||
if not self.library_view.restore_column_widths():
|
||||
@ -978,6 +980,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
self.status_bar.reset_info()
|
||||
self.location_view.setCurrentIndex(self.location_view.model().index(0))
|
||||
self.eject_action.setEnabled(False)
|
||||
self.refresh_ondevice_info (clear_info = True)
|
||||
|
||||
def info_read(self, job):
|
||||
'''
|
||||
@ -1012,10 +1015,13 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
return
|
||||
mainlist, cardalist, cardblist = job.result
|
||||
self.memory_view.set_database(mainlist)
|
||||
self.memory_view.model().set_book_in_library_func(self.book_in_library, 'main')
|
||||
self.memory_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
||||
self.card_a_view.set_database(cardalist)
|
||||
self.card_a_view.model().set_book_in_library_func(self.book_in_library, 'carda')
|
||||
self.card_a_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
||||
self.card_b_view.set_database(cardblist)
|
||||
self.card_b_view.model().set_book_in_library_func(self.book_in_library, 'cardb')
|
||||
self.card_b_view.set_editable(self.device_manager.device.CAN_SET_METADATA)
|
||||
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||
view.sortByColumn(3, Qt.DescendingOrder)
|
||||
@ -1025,6 +1031,18 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
view.resize_on_select = not view.isVisible()
|
||||
self.sync_news()
|
||||
self.sync_catalogs()
|
||||
self.refresh_ondevice_info()
|
||||
|
||||
############################################################################
|
||||
### Force the library view to refresh, taking into consideration books information
|
||||
def refresh_ondevice_info(self, clear_flags = False):
|
||||
# self.library_view.model().db.set_all_ondevice('')
|
||||
# if not clear_flags:
|
||||
# for id in self.library_view.model().db:
|
||||
# self.library_view.model().db.set_book_on_device_string(id, index_is_id=True))
|
||||
self.library_view.model().refresh()
|
||||
############################################################################
|
||||
|
||||
############################################################################
|
||||
|
||||
######################### Fetch annotations ################################
|
||||
@ -2250,7 +2268,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
def library_moved(self, newloc):
|
||||
if newloc is None: return
|
||||
db = LibraryDatabase2(newloc)
|
||||
db.set_book_on_device_func(self.book_on_device)
|
||||
self.library_view.set_database(db)
|
||||
self.library_view.model().set_book_on_device_func(self.book_on_device)
|
||||
self.status_bar.clearMessage()
|
||||
self.search.clear_to_help()
|
||||
self.status_bar.reset_info()
|
||||
|
@ -518,6 +518,7 @@ class ResultCache(SearchQueryParser):
|
||||
try:
|
||||
self._data[id] = db.conn.get('SELECT * from meta2 WHERE id=?', (id,))[0]
|
||||
self._data[id].append(db.has_cover(id, index_is_id=True))
|
||||
self._data[id].append(db.book_on_device_string(id, index_is_id=True))
|
||||
except IndexError:
|
||||
return None
|
||||
try:
|
||||
@ -533,6 +534,7 @@ class ResultCache(SearchQueryParser):
|
||||
for id in ids:
|
||||
self._data[id] = db.conn.get('SELECT * from meta2 WHERE id=?', (id,))[0]
|
||||
self._data[id].append(db.has_cover(id, index_is_id=True))
|
||||
self._data[id].append(db.book_on_device_string(id, index_is_id=True))
|
||||
self._map[0:0] = ids
|
||||
self._map_filtered[0:0] = ids
|
||||
|
||||
@ -553,6 +555,7 @@ class ResultCache(SearchQueryParser):
|
||||
for item in self._data:
|
||||
if item is not None:
|
||||
item.append(db.has_cover(item[0], index_is_id=True))
|
||||
item.append(db.book_on_device_string(item[0], index_is_id=True))
|
||||
self._map = [i[0] for i in self._data if i is not None]
|
||||
if field is not None:
|
||||
self.sort(field, ascending)
|
||||
|
@ -1070,6 +1070,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
return [ (i[0], i[1]) for i in \
|
||||
self.conn.get('SELECT id, name FROM tags')]
|
||||
|
||||
def all_titles(self):
|
||||
return [ (i[0], i[1]) for i in \
|
||||
self.conn.get('SELECT id, title FROM books')]
|
||||
|
||||
def conversion_options(self, id, format):
|
||||
data = self.conn.get('SELECT data FROM conversion_options WHERE book=? AND format=?', (id, format.upper()), all=False)
|
||||
|
@ -219,6 +219,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
self.FIELD_MAP[col] = base = base+1
|
||||
|
||||
self.FIELD_MAP['cover'] = base+1
|
||||
self.FIELD_MAP['ondevice'] = base+2
|
||||
|
||||
script = '''
|
||||
DROP VIEW IF EXISTS meta2;
|
||||
@ -230,6 +231,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
self.conn.executescript(script)
|
||||
self.conn.commit()
|
||||
|
||||
self.book_on_device_func = None
|
||||
self.data = ResultCache(self.FIELD_MAP, self.custom_column_label_map)
|
||||
self.search = self.data.search
|
||||
self.refresh = functools.partial(self.data.refresh, self)
|
||||
@ -465,6 +467,27 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
im = PILImage.open(f)
|
||||
im.convert('RGB').save(path, 'JPEG')
|
||||
|
||||
def book_on_device(self, index, index_is_id=False):
|
||||
if self.book_on_device_func:
|
||||
return self.book_on_device_func(index, index_is_id)
|
||||
return None
|
||||
|
||||
def book_on_device_string(self, index, index_is_id=False):
|
||||
loc = []
|
||||
on = self.book_on_device(index, index_is_id)
|
||||
if on is not None:
|
||||
m, a, b = on
|
||||
if m is not None:
|
||||
loc.append(_('Main'))
|
||||
if a is not None:
|
||||
loc.append(_('Card A'))
|
||||
if b is not None:
|
||||
loc.append(_('Card B'))
|
||||
return ', '.join(loc)
|
||||
|
||||
def set_book_on_device_func(self, func):
|
||||
self.book_on_device_func = func
|
||||
|
||||
def all_formats(self):
|
||||
formats = self.conn.get('SELECT DISTINCT format from data')
|
||||
if not formats:
|
||||
|
Loading…
x
Reference in New Issue
Block a user