mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Make column order sortable in book list
This commit is contained in:
parent
3a15dea106
commit
a30a10dac8
@ -17,6 +17,8 @@ import calibre.resources as resources
|
|||||||
|
|
||||||
NONE = QVariant() #: Null value to return from the data function of item models
|
NONE = QVariant() #: Null value to return from the data function of item models
|
||||||
|
|
||||||
|
ALL_COLUMNS = ['title', 'authors', 'size', 'timestamp', 'rating', 'publisher', 'tags', 'series']
|
||||||
|
|
||||||
def _config():
|
def _config():
|
||||||
c = Config('gui', 'preferences for the calibre GUI')
|
c = Config('gui', 'preferences for the calibre GUI')
|
||||||
c.add_opt('frequently_used_directories', default=[],
|
c.add_opt('frequently_used_directories', default=[],
|
||||||
@ -47,6 +49,10 @@ def _config():
|
|||||||
help=_('Options for the LRF ebook viewer'))
|
help=_('Options for the LRF ebook viewer'))
|
||||||
c.add_opt('internally_viewed_formats', default=['LRF', 'EPUB', 'LIT', 'MOBI', 'PRC', 'HTML', 'FB2'],
|
c.add_opt('internally_viewed_formats', default=['LRF', 'EPUB', 'LIT', 'MOBI', 'PRC', 'HTML', 'FB2'],
|
||||||
help=_('Formats that are viewed using the internal viewer'))
|
help=_('Formats that are viewed using the internal viewer'))
|
||||||
|
c.add_opt('column_map', default=ALL_COLUMNS,
|
||||||
|
help=_('Columns to be displayed in the book list'))
|
||||||
|
c.add_opt('autolaunch_server', default=False, help=_('Automatically launch content server on application startup'))
|
||||||
|
|
||||||
return ConfigProxy(c)
|
return ConfigProxy(c)
|
||||||
|
|
||||||
config = _config()
|
config = _config()
|
||||||
|
@ -2,21 +2,24 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
import os, re
|
import os, re
|
||||||
|
|
||||||
from PyQt4.QtGui import QDialog, QMessageBox, QListWidgetItem, QIcon
|
from PyQt4.QtGui import QDialog, QMessageBox, QListWidgetItem, QIcon, \
|
||||||
from PyQt4.QtCore import SIGNAL, QTimer, Qt, QSize, QVariant
|
QDesktopServices, QVBoxLayout, QLabel, QPlainTextEdit
|
||||||
|
from PyQt4.QtCore import SIGNAL, QTimer, Qt, QSize, QVariant, QUrl
|
||||||
|
|
||||||
from calibre import islinux
|
from calibre import islinux
|
||||||
from calibre.gui2.dialogs.config_ui import Ui_Dialog
|
from calibre.gui2.dialogs.config_ui import Ui_Dialog
|
||||||
from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config, warning_dialog
|
from calibre.gui2 import qstring_to_unicode, choose_dir, error_dialog, config, \
|
||||||
|
warning_dialog, ALL_COLUMNS
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs
|
||||||
from calibre.gui2.widgets import FilenamePattern
|
from calibre.gui2.widgets import FilenamePattern
|
||||||
|
from calibre.gui2.library import BooksModel
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
from calibre.ebooks.epub.iterator import is_supported
|
from calibre.ebooks.epub.iterator import is_supported
|
||||||
|
from calibre.library import server_config
|
||||||
|
|
||||||
class ConfigDialog(QDialog, Ui_Dialog):
|
class ConfigDialog(QDialog, Ui_Dialog):
|
||||||
|
|
||||||
def __init__(self, window, db, columns):
|
def __init__(self, window, db, server=None):
|
||||||
QDialog.__init__(self, window)
|
QDialog.__init__(self, window)
|
||||||
Ui_Dialog.__init__(self)
|
Ui_Dialog.__init__(self)
|
||||||
self.ICON_SIZES = {0:QSize(48, 48), 1:QSize(32,32), 2:QSize(24,24)}
|
self.ICON_SIZES = {0:QSize(48, 48), 1:QSize(32,32), 2:QSize(24,24)}
|
||||||
@ -24,8 +27,9 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
self.item1 = QListWidgetItem(QIcon(':/images/metadata.svg'), _('General'), self.category_list)
|
self.item1 = QListWidgetItem(QIcon(':/images/metadata.svg'), _('General'), self.category_list)
|
||||||
self.item2 = QListWidgetItem(QIcon(':/images/lookfeel.svg'), _('Interface'), self.category_list)
|
self.item2 = QListWidgetItem(QIcon(':/images/lookfeel.svg'), _('Interface'), self.category_list)
|
||||||
self.item3 = QListWidgetItem(QIcon(':/images/view.svg'), _('Advanced'), self.category_list)
|
self.item3 = QListWidgetItem(QIcon(':/images/view.svg'), _('Advanced'), self.category_list)
|
||||||
|
self.item4 = QListWidgetItem(QIcon(':/images/network-server.svg'), _('Content\nServer'), self.category_list)
|
||||||
self.db = db
|
self.db = db
|
||||||
self.current_cols = columns
|
self.server = None
|
||||||
path = prefs['library_path']
|
path = prefs['library_path']
|
||||||
self.location.setText(path if path else '')
|
self.location.setText(path if path else '')
|
||||||
self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse)
|
self.connect(self.browse_button, SIGNAL('clicked(bool)'), self.browse)
|
||||||
@ -46,13 +50,17 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
if not islinux:
|
if not islinux:
|
||||||
self.dirs_box.setVisible(False)
|
self.dirs_box.setVisible(False)
|
||||||
|
|
||||||
for hidden, hdr in self.current_cols:
|
column_map = config['column_map']
|
||||||
item = QListWidgetItem(hdr, self.columns)
|
for col in column_map + [i for i in ALL_COLUMNS if i not in column_map]:
|
||||||
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable)
|
item = QListWidgetItem(BooksModel.headers[col], self.columns)
|
||||||
if hidden:
|
item.setData(Qt.UserRole, QVariant(col))
|
||||||
item.setCheckState(Qt.Unchecked)
|
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
||||||
else:
|
if col in column_map:
|
||||||
item.setCheckState(Qt.Checked)
|
item.setCheckState(Qt.Checked)
|
||||||
|
else:
|
||||||
|
item.setCheckState(Qt.Unchecked)
|
||||||
|
self.connect(self.column_up, SIGNAL('clicked()'), self.up_column)
|
||||||
|
self.connect(self.column_down, SIGNAL('clicked()'), self.down_column)
|
||||||
|
|
||||||
self.filename_pattern = FilenamePattern(self)
|
self.filename_pattern = FilenamePattern(self)
|
||||||
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
|
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
|
||||||
@ -97,7 +105,74 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
added_html = ext == 'html'
|
added_html = ext == 'html'
|
||||||
self.viewer.sortItems()
|
self.viewer.sortItems()
|
||||||
|
|
||||||
|
self.start.setEnabled(not getattr(self.server, 'is_running', False))
|
||||||
|
self.test.setEnabled(not self.start.isEnabled())
|
||||||
|
self.stop.setDisabled(self.start.isEnabled())
|
||||||
|
self.connect(self.start, SIGNAL('clicked()'), self.start_server)
|
||||||
|
self.connect(self.view_logs, SIGNAL('clicked()'), self.view_server_logs)
|
||||||
|
self.connect(self.stop, SIGNAL('clicked()'), self.stop_server)
|
||||||
|
self.connect(self.test, SIGNAL('clicked()'), self.test_server)
|
||||||
|
opts = server_config().parse()
|
||||||
|
self.port.setValue(opts.port)
|
||||||
|
self.username.setText(opts.username)
|
||||||
|
self.password.setText(opts.password if opts.password else '')
|
||||||
|
self.auto_launch.setChecked(config['autolaunch_server'])
|
||||||
|
|
||||||
|
def up_column(self):
|
||||||
|
idx = self.columns.currentRow()
|
||||||
|
if idx > 0:
|
||||||
|
self.columns.insertItem(idx-1, self.columns.takeItem(idx))
|
||||||
|
self.columns.setCurrentRow(idx-1)
|
||||||
|
|
||||||
|
def down_column(self):
|
||||||
|
idx = self.columns.currentRow()
|
||||||
|
if idx < self.columns.count()-1:
|
||||||
|
self.columns.insertItem(idx+1, self.columns.takeItem(idx))
|
||||||
|
self.columns.setCurrentRow(idx+1)
|
||||||
|
|
||||||
|
def view_server_logs(self):
|
||||||
|
from calibre.library.server import log_access_file, log_error_file
|
||||||
|
d = QDialog(self)
|
||||||
|
d.resize(QSize(800, 600))
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
d.setLayout(layout)
|
||||||
|
layout.addWidget(QLabel(_('Error log:')))
|
||||||
|
el = QPlainTextEdit(d)
|
||||||
|
layout.addWidget(el)
|
||||||
|
el.setPlainText(open(log_error_file, 'rb').read().decode('utf8', 'replace'))
|
||||||
|
layout.addWidget(QLabel(_('Access log:')))
|
||||||
|
al = QPlainTextEdit(d)
|
||||||
|
layout.addWidget(al)
|
||||||
|
al.setPlainText(open(log_access_file, 'rb').read().decode('utf8', 'replace'))
|
||||||
|
d.show()
|
||||||
|
|
||||||
|
def set_server_options(self):
|
||||||
|
c = server_config()
|
||||||
|
c.set('port', self.port.value())
|
||||||
|
c.set('username', unicode(self.username.text()).strip())
|
||||||
|
p = unicode(self.password.text()).strip()
|
||||||
|
if not p:
|
||||||
|
p = None
|
||||||
|
c.set('password', p)
|
||||||
|
|
||||||
|
def start_server(self):
|
||||||
|
self.set_server_options()
|
||||||
|
from calibre.library.server import start_threaded_server
|
||||||
|
self.server = start_threaded_server(self.db, server_config().parse())
|
||||||
|
self.start.setEnabled(False)
|
||||||
|
self.test.setEnabled(True)
|
||||||
|
self.stop.setEnabled(True)
|
||||||
|
|
||||||
|
def stop_server(self):
|
||||||
|
from calibre.library.server import stop_threaded_server
|
||||||
|
stop_threaded_server(self.server)
|
||||||
|
self.server = None
|
||||||
|
self.start.setEnabled(True)
|
||||||
|
self.test.setEnabled(False)
|
||||||
|
self.stop.setEnabled(False)
|
||||||
|
|
||||||
|
def test_server(self):
|
||||||
|
QDesktopServices.openUrl(QUrl('http://127.0.0.1:'+str(self.port.value())))
|
||||||
|
|
||||||
def compact(self, toggled):
|
def compact(self, toggled):
|
||||||
d = Vacuum(self, self.db)
|
d = Vacuum(self, self.db)
|
||||||
@ -123,7 +198,10 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
config['new_version_notification'] = bool(self.new_version_notification.isChecked())
|
config['new_version_notification'] = bool(self.new_version_notification.isChecked())
|
||||||
prefs['network_timeout'] = int(self.timeout.value())
|
prefs['network_timeout'] = int(self.timeout.value())
|
||||||
path = qstring_to_unicode(self.location.text())
|
path = qstring_to_unicode(self.location.text())
|
||||||
self.final_columns = [self.columns.item(i).checkState() == Qt.Checked for i in range(self.columns.count())]
|
cols = []
|
||||||
|
for i in range(self.columns.count()):
|
||||||
|
cols.append(unicode(self.columns.item(i).data(Qt.UserRole).toString()))
|
||||||
|
config['column_map'] = cols
|
||||||
config['toolbar_icon_size'] = self.ICON_SIZES[self.toolbar_button_size.currentIndex()]
|
config['toolbar_icon_size'] = self.ICON_SIZES[self.toolbar_button_size.currentIndex()]
|
||||||
config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked())
|
config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked())
|
||||||
config['confirm_delete'] = bool(self.confirm_delete.isChecked())
|
config['confirm_delete'] = bool(self.confirm_delete.isChecked())
|
||||||
@ -133,6 +211,11 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
config['save_to_disk_single_format'] = BOOK_EXTENSIONS[self.single_format.currentIndex()]
|
config['save_to_disk_single_format'] = BOOK_EXTENSIONS[self.single_format.currentIndex()]
|
||||||
config['cover_flow_queue_length'] = self.cover_browse.value()
|
config['cover_flow_queue_length'] = self.cover_browse.value()
|
||||||
prefs['language'] = str(self.language.itemData(self.language.currentIndex()).toString())
|
prefs['language'] = str(self.language.itemData(self.language.currentIndex()).toString())
|
||||||
|
config['autolaunch_server'] = self.auto_launch.isChecked()
|
||||||
|
sc = server_config()
|
||||||
|
sc.set('username', unicode(self.username.text()).strip())
|
||||||
|
sc.set('password', unicode(self.password.text()).strip())
|
||||||
|
sc.set('port', self.port.value())
|
||||||
of = str(self.output_format.currentText())
|
of = str(self.output_format.currentText())
|
||||||
fmts = []
|
fmts = []
|
||||||
for i in range(self.viewer.count()):
|
for i in range(self.viewer.count()):
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>709</width>
|
<width>709</width>
|
||||||
<height>676</height>
|
<height>840</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle" >
|
||||||
@ -67,12 +67,6 @@
|
|||||||
<property name="viewMode" >
|
<property name="viewMode" >
|
||||||
<enum>QListView::IconMode</enum>
|
<enum>QListView::IconMode</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="uniformItemSizes" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="currentRow" >
|
|
||||||
<number>-1</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -433,15 +427,61 @@
|
|||||||
<property name="title" >
|
<property name="title" >
|
||||||
<string>Select visible &columns in library view</string>
|
<string>Select visible &columns in library view</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="_4" >
|
<layout class="QVBoxLayout" name="verticalLayout_4" >
|
||||||
<item row="0" column="0" >
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
||||||
|
<item>
|
||||||
<widget class="QListWidget" name="columns" >
|
<widget class="QListWidget" name="columns" >
|
||||||
<property name="selectionMode" >
|
<property name="alternatingRowColors" >
|
||||||
<enum>QAbstractItemView::NoSelection</enum>
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior" >
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" >
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="column_up" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="../images.qrc" >
|
||||||
|
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="column_down" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="../images.qrc" >
|
||||||
|
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_3" >
|
<widget class="QGroupBox" name="groupBox_3" >
|
||||||
<property name="title" >
|
<property name="title" >
|
||||||
<string>Use internal &viewer for the following formats:</string>
|
<string>Use internal &viewer for the following formats:</string>
|
||||||
@ -449,6 +489,9 @@
|
|||||||
<layout class="QGridLayout" name="gridLayout_4" >
|
<layout class="QGridLayout" name="gridLayout_4" >
|
||||||
<item row="0" column="0" >
|
<item row="0" column="0" >
|
||||||
<widget class="QListWidget" name="viewer" >
|
<widget class="QListWidget" name="viewer" >
|
||||||
|
<property name="alternatingRowColors" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
<property name="selectionMode" >
|
<property name="selectionMode" >
|
||||||
<enum>QAbstractItemView::NoSelection</enum>
|
<enum>QAbstractItemView::NoSelection</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -528,6 +571,142 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="page_4" >
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_9" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>calibre contains a network server that allows you to access your book collection using a browser from anywhere in the world. Any changes to the settings will only take effect after a server restart.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap" >
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_5" >
|
||||||
|
<item row="0" column="0" >
|
||||||
|
<widget class="QLabel" name="label_10" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Server &port:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>port</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" >
|
||||||
|
<widget class="QSpinBox" name="port" >
|
||||||
|
<property name="minimum" >
|
||||||
|
<number>1025</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum" >
|
||||||
|
<number>16000</number>
|
||||||
|
</property>
|
||||||
|
<property name="value" >
|
||||||
|
<number>8080</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" >
|
||||||
|
<widget class="QLabel" name="label_11" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Username:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>username</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" >
|
||||||
|
<widget class="QLineEdit" name="username" />
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" >
|
||||||
|
<widget class="QLabel" name="label_12" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Password:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy" >
|
||||||
|
<cstring>password</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" >
|
||||||
|
<widget class="QLineEdit" name="password" >
|
||||||
|
<property name="toolTip" >
|
||||||
|
<string>If you leave the password blank, anyone will be able to access your book collection using the web interface.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2" >
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="start" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Start Server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="stop" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>St&op Server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0" >
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="test" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>&Test Server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="auto_launch" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Run server &automatically on startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="view_logs" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>View &server logs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer" >
|
||||||
|
<property name="orientation" >
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0" >
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
@ -5,18 +5,18 @@ from datetime import timedelta, datetime
|
|||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
from math import cos, sin, pi
|
from math import cos, sin, pi
|
||||||
from itertools import repeat
|
from PyQt4.QtGui import QTableView, QAbstractItemView, QColor, \
|
||||||
from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \
|
|
||||||
QItemDelegate, QPainterPath, QLinearGradient, QBrush, \
|
QItemDelegate, QPainterPath, QLinearGradient, QBrush, \
|
||||||
QPen, QStyle, QPainter, QLineEdit, \
|
QPen, QStyle, QPainter, QLineEdit, \
|
||||||
QPalette, QImage, QApplication
|
QPalette, QImage, QApplication
|
||||||
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \
|
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \
|
||||||
QCoreApplication, SIGNAL, QObject, QSize, QModelIndex
|
SIGNAL, QObject, QSize, QModelIndex
|
||||||
|
|
||||||
from calibre import strftime
|
from calibre import strftime
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
from calibre.library.database import LibraryDatabase, text_to_tokens
|
from calibre.library.database2 import FIELD_MAP
|
||||||
from calibre.gui2 import NONE, TableView, qstring_to_unicode, config
|
from calibre.gui2 import NONE, TableView, qstring_to_unicode, config
|
||||||
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
|
||||||
class LibraryDelegate(QItemDelegate):
|
class LibraryDelegate(QItemDelegate):
|
||||||
COLOR = QColor("blue")
|
COLOR = QColor("blue")
|
||||||
@ -85,6 +85,18 @@ class BooksModel(QAbstractTableModel):
|
|||||||
[1000,900,500,400,100,90,50,40,10,9,5,4,1],
|
[1000,900,500,400,100,90,50,40,10,9,5,4,1],
|
||||||
["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
|
["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
'title' : _("Title"),
|
||||||
|
'authors' : _("Author(s)"),
|
||||||
|
'size' : _("Size (MB)"),
|
||||||
|
'timestamp' : _("Date"),
|
||||||
|
'rating' : _('Rating'),
|
||||||
|
'publisher' : _("Publisher"),
|
||||||
|
'tags' : _("Tags"),
|
||||||
|
'series' : _("Series"),
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def roman(cls, num):
|
def roman(cls, num):
|
||||||
if num <= 0 or num >= 4000 or int(num) != num:
|
if num <= 0 or num >= 4000 or int(num) != num:
|
||||||
@ -99,10 +111,10 @@ class BooksModel(QAbstractTableModel):
|
|||||||
def __init__(self, parent=None, buffer=40):
|
def __init__(self, parent=None, buffer=40):
|
||||||
QAbstractTableModel.__init__(self, parent)
|
QAbstractTableModel.__init__(self, parent)
|
||||||
self.db = None
|
self.db = None
|
||||||
self.cols = ['title', 'authors', 'size', 'date', 'rating', 'publisher', 'tags', 'series']
|
self.column_map = config['column_map']
|
||||||
self.editable_cols = [0, 1, 4, 5, 6, 7]
|
self.editable_cols = ['title', 'authors', 'rating', 'publisher', 'tags', 'series']
|
||||||
self.default_image = QImage(':/images/book.svg')
|
self.default_image = QImage(':/images/book.svg')
|
||||||
self.sorted_on = (3, Qt.AscendingOrder)
|
self.sorted_on = ('timestamp', Qt.AscendingOrder)
|
||||||
self.last_search = '' # The last search performed on this model
|
self.last_search = '' # The last search performed on this model
|
||||||
self.read_config()
|
self.read_config()
|
||||||
self.buffer_size = buffer
|
self.buffer_size = buffer
|
||||||
@ -114,14 +126,15 @@ class BooksModel(QAbstractTableModel):
|
|||||||
|
|
||||||
def read_config(self):
|
def read_config(self):
|
||||||
self.use_roman_numbers = config['use_roman_numerals_for_series_number']
|
self.use_roman_numbers = config['use_roman_numerals_for_series_number']
|
||||||
|
cols = config['column_map']
|
||||||
|
if cols != self.column_map:
|
||||||
|
self.column_map = cols
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
|
||||||
def set_database(self, db):
|
def set_database(self, db):
|
||||||
if isinstance(db, (QString, basestring)):
|
|
||||||
if isinstance(db, QString):
|
|
||||||
db = qstring_to_unicode(db)
|
|
||||||
db = LibraryDatabase(os.path.expanduser(db))
|
|
||||||
self.db = db
|
self.db = db
|
||||||
|
self.build_data_convertors()
|
||||||
|
|
||||||
def refresh_ids(self, ids, current_row=-1):
|
def refresh_ids(self, ids, current_row=-1):
|
||||||
rows = self.db.refresh_ids(ids)
|
rows = self.db.refresh_ids(ids)
|
||||||
@ -151,7 +164,8 @@ class BooksModel(QAbstractTableModel):
|
|||||||
def save_to_disk(self, rows, path, single_dir=False, single_format=None):
|
def save_to_disk(self, rows, path, single_dir=False, single_format=None):
|
||||||
rows = [row.row() for row in rows]
|
rows = [row.row() for row in rows]
|
||||||
if single_format is None:
|
if single_format is None:
|
||||||
return self.db.export_to_dir(path, rows, self.sorted_on[0] == 1, single_dir=single_dir)
|
return self.db.export_to_dir(path, rows, self.sorted_on[0] == 'authors',
|
||||||
|
single_dir=single_dir)
|
||||||
else:
|
else:
|
||||||
return self.db.export_single_format_to_dir(path, rows, single_format)
|
return self.db.export_single_format_to_dir(path, rows, single_format)
|
||||||
|
|
||||||
@ -166,9 +180,6 @@ class BooksModel(QAbstractTableModel):
|
|||||||
self.clear_caches()
|
self.clear_caches()
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def search_tokens(self, text):
|
|
||||||
return text_to_tokens(text)
|
|
||||||
|
|
||||||
def books_added(self, num):
|
def books_added(self, num):
|
||||||
if num > 0:
|
if num > 0:
|
||||||
self.beginInsertRows(QModelIndex(), 0, num-1)
|
self.beginInsertRows(QModelIndex(), 0, num-1)
|
||||||
@ -185,29 +196,27 @@ class BooksModel(QAbstractTableModel):
|
|||||||
if not self.db:
|
if not self.db:
|
||||||
return
|
return
|
||||||
ascending = order == Qt.AscendingOrder
|
ascending = order == Qt.AscendingOrder
|
||||||
self.db.sort(self.cols[col], ascending)
|
self.db.sort(self.column_map[col], ascending)
|
||||||
self.research()
|
self.research(reset=False)
|
||||||
if reset:
|
if reset:
|
||||||
self.clear_caches()
|
self.clear_caches()
|
||||||
self.reset()
|
self.reset()
|
||||||
self.sorted_on = (col, order)
|
self.sorted_on = (self.column_map[col], order)
|
||||||
|
|
||||||
def resort(self, reset=True):
|
def resort(self, reset=True):
|
||||||
self.sort(*self.sorted_on, **dict(reset=reset))
|
try:
|
||||||
|
col = self.column_map.index(self.sorted_on[0])
|
||||||
|
except:
|
||||||
|
col = 0
|
||||||
|
self.sort(col, self.sorted_on[1], reset=reset)
|
||||||
|
|
||||||
def research(self, reset=True):
|
def research(self, reset=True):
|
||||||
self.search(self.last_search, False, reset=reset)
|
self.search(self.last_search, False, reset=reset)
|
||||||
|
|
||||||
def database_needs_migration(self):
|
|
||||||
path = os.path.expanduser('~/library.db')
|
|
||||||
return self.db.is_empty() and \
|
|
||||||
os.path.exists(path) and\
|
|
||||||
LibraryDatabase.sizeof_old_database(path) > 0
|
|
||||||
|
|
||||||
def columnCount(self, parent):
|
def columnCount(self, parent):
|
||||||
if parent and parent.isValid():
|
if parent and parent.isValid():
|
||||||
return 0
|
return 0
|
||||||
return len(self.cols)
|
return len(self.column_map)
|
||||||
|
|
||||||
def rowCount(self, parent):
|
def rowCount(self, parent):
|
||||||
if parent and parent.isValid():
|
if parent and parent.isValid():
|
||||||
@ -374,65 +383,77 @@ class BooksModel(QAbstractTableModel):
|
|||||||
img = self.default_image
|
img = self.default_image
|
||||||
return img
|
return img
|
||||||
|
|
||||||
def data(self, index, role):
|
def build_data_convertors(self):
|
||||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
|
||||||
row, col = index.row(), index.column()
|
tidx = FIELD_MAP['title']
|
||||||
if col == 0:
|
aidx = FIELD_MAP['authors']
|
||||||
text = self.db.title(row)
|
sidx = FIELD_MAP['size']
|
||||||
if text:
|
ridx = FIELD_MAP['rating']
|
||||||
return QVariant(text)
|
pidx = FIELD_MAP['publisher']
|
||||||
elif col == 1:
|
tmdx = FIELD_MAP['timestamp']
|
||||||
au = self.db.authors(row)
|
srdx = FIELD_MAP['series']
|
||||||
|
tgdx = FIELD_MAP['tags']
|
||||||
|
siix = FIELD_MAP['series_index']
|
||||||
|
|
||||||
|
def authors(r):
|
||||||
|
au = self.db.data[r][aidx]
|
||||||
if au:
|
if au:
|
||||||
au = [a.strip().replace('|', ',') for a in au.split(',')]
|
au = [a.strip().replace('|', ',') for a in au.split(',')]
|
||||||
return QVariant("\n".join(au))
|
return '\n'.join(au)
|
||||||
elif col == 2:
|
|
||||||
size = self.db.max_size(row)
|
def timestamp(r):
|
||||||
if size:
|
dt = self.db.data[r][tmdx]
|
||||||
return QVariant(BooksView.human_readable(size))
|
|
||||||
elif col == 3:
|
|
||||||
dt = self.db.timestamp(row)
|
|
||||||
if dt:
|
if dt:
|
||||||
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
||||||
return QVariant(strftime(BooksView.TIME_FMT, dt.timetuple()))
|
return strftime(BooksView.TIME_FMT, dt.timetuple())
|
||||||
elif col == 4:
|
|
||||||
r = self.db.rating(row)
|
def rating(r):
|
||||||
|
r = self.db.data[r][ridx]
|
||||||
r = r/2 if r else 0
|
r = r/2 if r else 0
|
||||||
return QVariant(r)
|
return r
|
||||||
elif col == 5:
|
|
||||||
pub = self.db.publisher(row)
|
def publisher(r):
|
||||||
|
pub = self.db.data[r][pidx]
|
||||||
if pub:
|
if pub:
|
||||||
return QVariant(pub)
|
return pub
|
||||||
elif col == 6:
|
|
||||||
tags = self.db.tags(row)
|
def tags(r):
|
||||||
|
tags = self.db.data[r][tgdx]
|
||||||
if tags:
|
if tags:
|
||||||
return QVariant(', '.join(tags.split(',')))
|
return ', '.join(tags.split(','))
|
||||||
elif col == 7:
|
|
||||||
series = self.db.series(row)
|
def series(r):
|
||||||
|
series = self.db.data[r][srdx]
|
||||||
if series:
|
if series:
|
||||||
return QVariant(series + ' [%d]'%self.db.series_index(row))
|
return series + ' [%d]'%self.db.data[r][siix]
|
||||||
return NONE
|
|
||||||
elif role == Qt.TextAlignmentRole and index.column() in [2, 3, 4]:
|
self.dc = {
|
||||||
return QVariant(Qt.AlignRight | Qt.AlignVCenter)
|
'title' : lambda r : self.db.data[r][tidx],
|
||||||
elif role == Qt.ToolTipRole and index.isValid():
|
'authors' : authors,
|
||||||
if index.column() in self.editable_cols:
|
'size' : lambda r : self.db.data[r][sidx],
|
||||||
return QVariant(_("Double click to <b>edit</b> me<br><br>"))
|
'timestamp': timestamp,
|
||||||
|
'rating' : rating,
|
||||||
|
'publisher': publisher,
|
||||||
|
'tags' : tags,
|
||||||
|
'series' : series,
|
||||||
|
}
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if role in (Qt.DisplayRole, Qt.EditRole):
|
||||||
|
ans = self.dc[self.column_map[index.column()]](index.row())
|
||||||
|
return NONE if ans is None else QVariant(ans)
|
||||||
|
elif role == Qt.TextAlignmentRole and self.column_map[index.column()] in ('size', 'timestamp', 'rating'):
|
||||||
|
return QVariant(Qt.AlignCenter | Qt.AlignVCenter)
|
||||||
|
#elif role == Qt.ToolTipRole and index.isValid():
|
||||||
|
# if self.column_map[index.column()] in self.editable_cols:
|
||||||
|
# return QVariant(_("Double click to <b>edit</b> me<br><br>"))
|
||||||
return NONE
|
return NONE
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
def headerData(self, section, orientation, role):
|
||||||
if role != Qt.DisplayRole:
|
if role != Qt.DisplayRole:
|
||||||
return NONE
|
return NONE
|
||||||
text = ""
|
|
||||||
if orientation == Qt.Horizontal:
|
if orientation == Qt.Horizontal:
|
||||||
if section == 0: text = _("Title")
|
return QVariant(self.headers[self.column_map[section]])
|
||||||
elif section == 1: text = _("Author(s)")
|
|
||||||
elif section == 2: text = _("Size (MB)")
|
|
||||||
elif section == 3: text = _("Date")
|
|
||||||
elif section == 4: text = _("Rating")
|
|
||||||
elif section == 5: text = _("Publisher")
|
|
||||||
elif section == 6: text = _("Tags")
|
|
||||||
elif section == 7: text = _("Series")
|
|
||||||
return QVariant(text)
|
|
||||||
else:
|
else:
|
||||||
return QVariant(section+1)
|
return QVariant(section+1)
|
||||||
|
|
||||||
@ -447,14 +468,15 @@ class BooksModel(QAbstractTableModel):
|
|||||||
done = False
|
done = False
|
||||||
if role == Qt.EditRole:
|
if role == Qt.EditRole:
|
||||||
row, col = index.row(), index.column()
|
row, col = index.row(), index.column()
|
||||||
if col not in self.editable_cols:
|
column = self.column_map[col]
|
||||||
|
if column not in self.editable_cols:
|
||||||
return False
|
return False
|
||||||
val = unicode(value.toString().toUtf8(), 'utf-8').strip() if col != 4 else \
|
val = unicode(value.toString().toUtf8(), 'utf-8').strip() if column != 'rating' else \
|
||||||
int(value.toInt()[0])
|
int(value.toInt()[0])
|
||||||
if col == 4:
|
if col == 'rating':
|
||||||
val = 0 if val < 0 else 5 if val > 5 else val
|
val = 0 if val < 0 else 5 if val > 5 else val
|
||||||
val *= 2
|
val *= 2
|
||||||
if col == 7:
|
if col == 'series':
|
||||||
pat = re.compile(r'\[(\d+)\]')
|
pat = re.compile(r'\[(\d+)\]')
|
||||||
match = pat.search(val)
|
match = pat.search(val)
|
||||||
id = self.db.id(row)
|
id = self.db.id(row)
|
||||||
@ -465,12 +487,11 @@ class BooksModel(QAbstractTableModel):
|
|||||||
if val:
|
if val:
|
||||||
self.db.set_series(id, val)
|
self.db.set_series(id, val)
|
||||||
else:
|
else:
|
||||||
column = self.cols[col]
|
|
||||||
self.db.set(row, column, val)
|
self.db.set(row, column, val)
|
||||||
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
|
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
|
||||||
index, index)
|
index, index)
|
||||||
if col == self.sorted_on[0]:
|
if column == self.sorted_on[0]:
|
||||||
self.sort(col, self.sorted_on[1])
|
self.resort()
|
||||||
done = True
|
done = True
|
||||||
return done
|
return done
|
||||||
|
|
||||||
@ -495,8 +516,7 @@ class BooksView(TableView):
|
|||||||
self.setModel(self._model)
|
self.setModel(self._model)
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
if self.__class__.__name__ == 'BooksView': # Subclasses may not have rating as col 4
|
self.columns_sorted()
|
||||||
self.setItemDelegateForColumn(4, LibraryDelegate(self))
|
|
||||||
QObject.connect(self.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
QObject.connect(self.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
||||||
self._model.current_changed)
|
self._model.current_changed)
|
||||||
# Adding and removing rows should resize rows to contents
|
# Adding and removing rows should resize rows to contents
|
||||||
@ -506,6 +526,22 @@ class BooksView(TableView):
|
|||||||
QObject.connect(self.model(), SIGNAL('modelReset()'), self.resizeRowsToContents)
|
QObject.connect(self.model(), SIGNAL('modelReset()'), self.resizeRowsToContents)
|
||||||
self.set_visible_columns()
|
self.set_visible_columns()
|
||||||
|
|
||||||
|
def columns_sorted(self):
|
||||||
|
if self.__class__.__name__ == 'BooksView':
|
||||||
|
try:
|
||||||
|
idx = self._model.column_map.index('rating')
|
||||||
|
self.setItemDelegateForColumn(idx, LibraryDelegate(self))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def sortByColumn(self, colname, order):
|
||||||
|
try:
|
||||||
|
idx = self._model.column_map.index(colname)
|
||||||
|
except ValueError:
|
||||||
|
idx = 0
|
||||||
|
TableView.sortByColumn(self, idx, order)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def paths_from_event(cls, event):
|
def paths_from_event(cls, event):
|
||||||
'''
|
'''
|
||||||
@ -541,25 +577,6 @@ class BooksView(TableView):
|
|||||||
def close(self):
|
def close(self):
|
||||||
self._model.close()
|
self._model.close()
|
||||||
|
|
||||||
def migrate_database(self):
|
|
||||||
if self.model().database_needs_migration():
|
|
||||||
print 'Migrating database from pre 0.4.0 version'
|
|
||||||
path = os.path.abspath(os.path.expanduser('~/library.db'))
|
|
||||||
progress = QProgressDialog('Upgrading database from pre 0.4.0 version.<br>'+\
|
|
||||||
'The new database is stored in the file <b>'+self._model.db.dbpath,
|
|
||||||
QString(), 0, LibraryDatabase.sizeof_old_database(path),
|
|
||||||
self)
|
|
||||||
progress.setModal(True)
|
|
||||||
progress.setValue(0)
|
|
||||||
app = QCoreApplication.instance()
|
|
||||||
|
|
||||||
def meter(count):
|
|
||||||
progress.setValue(count)
|
|
||||||
app.processEvents()
|
|
||||||
progress.setWindowTitle('Upgrading database')
|
|
||||||
progress.show()
|
|
||||||
LibraryDatabase.import_old_database(path, self._model.db.conn, meter)
|
|
||||||
|
|
||||||
def connect_to_search_box(self, sb):
|
def connect_to_search_box(self, sb):
|
||||||
QObject.connect(sb, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'),
|
QObject.connect(sb, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'),
|
||||||
self._model.search)
|
self._model.search)
|
||||||
@ -583,6 +600,40 @@ class DeviceBooksView(BooksView):
|
|||||||
def connect_dirtied_signal(self, slot):
|
def connect_dirtied_signal(self, slot):
|
||||||
QObject.connect(self._model, SIGNAL('booklist_dirtied()'), slot)
|
QObject.connect(self._model, SIGNAL('booklist_dirtied()'), slot)
|
||||||
|
|
||||||
|
def sortByColumn(self, col, order):
|
||||||
|
TableView.sortByColumn(self, col, order)
|
||||||
|
|
||||||
|
class OnDeviceSearch(SearchQueryParser):
|
||||||
|
|
||||||
|
def __init__(self, model):
|
||||||
|
SearchQueryParser.__init__(self)
|
||||||
|
self.model = model
|
||||||
|
|
||||||
|
def universal_set(self):
|
||||||
|
return set(range(0, len(self.model.db)))
|
||||||
|
|
||||||
|
def get_matches(self, location, query):
|
||||||
|
location = location.lower().strip()
|
||||||
|
query = query.lower().strip()
|
||||||
|
if location not in ('title', 'authors', 'tags', 'all'):
|
||||||
|
return set([])
|
||||||
|
matches = set([])
|
||||||
|
locations = ['title', 'authors', 'tags'] if location == 'all' else [location]
|
||||||
|
q = {
|
||||||
|
'title' : lambda x : getattr(x, 'title').lower(),
|
||||||
|
'authors': lambda x: getattr(x, 'authors').lower(),
|
||||||
|
'tags':lambda x: ','.join(getattr(x, 'tags')).lower()
|
||||||
|
}
|
||||||
|
for i, v in enumerate(locations):
|
||||||
|
locations[i] = q[v]
|
||||||
|
for i, r in enumerate(self.model.db):
|
||||||
|
for loc in locations:
|
||||||
|
if query in loc(r):
|
||||||
|
matches.add(i)
|
||||||
|
break
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
||||||
class DeviceBooksModel(BooksModel):
|
class DeviceBooksModel(BooksModel):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@ -592,6 +643,7 @@ class DeviceBooksModel(BooksModel):
|
|||||||
self.sorted_map = []
|
self.sorted_map = []
|
||||||
self.unknown = str(self.trUtf8('Unknown'))
|
self.unknown = str(self.trUtf8('Unknown'))
|
||||||
self.marked_for_deletion = {}
|
self.marked_for_deletion = {}
|
||||||
|
self.search_engine = OnDeviceSearch(self)
|
||||||
|
|
||||||
|
|
||||||
def mark_for_deletion(self, job, rows):
|
def mark_for_deletion(self, job, rows):
|
||||||
@ -632,34 +684,22 @@ class DeviceBooksModel(BooksModel):
|
|||||||
|
|
||||||
|
|
||||||
def search(self, text, refinement, reset=True):
|
def search(self, text, refinement, reset=True):
|
||||||
tokens, OR = self.search_tokens(text)
|
if not text:
|
||||||
base = self.map if refinement else self.sorted_map
|
self.map = list(range(len(self.db)))
|
||||||
result = []
|
|
||||||
for i in base:
|
|
||||||
q = ['', self.db[i].title, self.db[i].authors, '', ', '.join(self.db[i].tags)] + list(repeat('', 10))
|
|
||||||
if OR:
|
|
||||||
add = False
|
|
||||||
for token in tokens:
|
|
||||||
if token.match(q):
|
|
||||||
add = True
|
|
||||||
break
|
|
||||||
if add:
|
|
||||||
result.append(i)
|
|
||||||
else:
|
else:
|
||||||
add = True
|
matches = self.search_engine.parse(text)
|
||||||
for token in tokens:
|
self.map = []
|
||||||
if not token.match(q):
|
for i in range(len(self.db)):
|
||||||
add = False
|
if i in matches:
|
||||||
break
|
self.map.append(i)
|
||||||
if add:
|
self.resort(reset=False)
|
||||||
result.append(i)
|
|
||||||
|
|
||||||
self.map = result
|
|
||||||
|
|
||||||
if reset:
|
if reset:
|
||||||
self.reset()
|
self.reset()
|
||||||
self.last_search = text
|
self.last_search = text
|
||||||
|
|
||||||
|
def resort(self, reset):
|
||||||
|
self.sort(self.sorted_on[0], self.sorted_on[1], reset=reset)
|
||||||
|
|
||||||
def sort(self, col, order, reset=True):
|
def sort(self, col, order, reset=True):
|
||||||
descending = order != Qt.AscendingOrder
|
descending = order != Qt.AscendingOrder
|
||||||
def strcmp(attr):
|
def strcmp(attr):
|
||||||
|
@ -87,6 +87,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
self.tb_wrapper = textwrap.TextWrapper(width=40)
|
self.tb_wrapper = textwrap.TextWrapper(width=40)
|
||||||
self.device_connected = False
|
self.device_connected = False
|
||||||
self.viewers = collections.deque()
|
self.viewers = collections.deque()
|
||||||
|
self.content_server = None
|
||||||
|
|
||||||
####################### Location View ########################
|
####################### Location View ########################
|
||||||
QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'),
|
QObject.connect(self.location_view, SIGNAL('location_selected(PyQt_PyObject)'),
|
||||||
@ -239,7 +240,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
os.remove(self.olddb.dbpath)
|
os.remove(self.olddb.dbpath)
|
||||||
self.olddb = None
|
self.olddb = None
|
||||||
prefs['library_path'] = self.library_path
|
prefs['library_path'] = self.library_path
|
||||||
self.library_view.sortByColumn(*dynamic.get('sort_column', (3, Qt.DescendingOrder)))
|
self.library_view.sortByColumn(*dynamic.get('sort_column', ('timestamp', Qt.DescendingOrder)))
|
||||||
if not self.library_view.restore_column_widths():
|
if not self.library_view.restore_column_widths():
|
||||||
self.library_view.resizeColumnsToContents()
|
self.library_view.resizeColumnsToContents()
|
||||||
self.library_view.resizeRowsToContents()
|
self.library_view.resizeRowsToContents()
|
||||||
@ -283,6 +284,12 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
self.news_menu.set_custom_feeds(self.library_view.model().db.get_feeds())
|
self.news_menu.set_custom_feeds(self.library_view.model().db.get_feeds())
|
||||||
|
|
||||||
|
if config['autolaunch_server']:
|
||||||
|
from calibre.library.server import start_threaded_server
|
||||||
|
from calibre.library import server_config
|
||||||
|
self.server = start_threaded_server(db, server_config().parse())
|
||||||
|
|
||||||
|
|
||||||
def toggle_cover_flow(self, show):
|
def toggle_cover_flow(self, show):
|
||||||
if show:
|
if show:
|
||||||
self.library_view.setCurrentIndex(self.library_view.currentIndex())
|
self.library_view.setCurrentIndex(self.library_view.currentIndex())
|
||||||
@ -1004,13 +1011,10 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
d = error_dialog(self, _('Cannot configure'), _('Cannot configure while there are running jobs.'))
|
d = error_dialog(self, _('Cannot configure'), _('Cannot configure while there are running jobs.'))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
return
|
return
|
||||||
columns = [(self.library_view.isColumnHidden(i), \
|
d = ConfigDialog(self, self.library_view.model().db, self.content_server)
|
||||||
self.library_view.model().headerData(i, Qt.Horizontal, Qt.DisplayRole).toString())\
|
|
||||||
for i in range(self.library_view.model().columnCount(None))]
|
|
||||||
d = ConfigDialog(self, self.library_view.model().db, columns)
|
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
self.content_server = d.server
|
||||||
if d.result() == d.Accepted:
|
if d.result() == d.Accepted:
|
||||||
self.library_view.set_visible_columns(d.final_columns)
|
|
||||||
self.tool_bar.setIconSize(config['toolbar_icon_size'])
|
self.tool_bar.setIconSize(config['toolbar_icon_size'])
|
||||||
self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if config['show_text_in_toolbar'] else Qt.ToolButtonIconOnly)
|
self.tool_bar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon if config['show_text_in_toolbar'] else Qt.ToolButtonIconOnly)
|
||||||
self.save_menu.actions()[2].setText(_('Save only %s format to disk')%config.get('save_to_disk_single_format').upper())
|
self.save_menu.actions()[2].setText(_('Save only %s format to disk')%config.get('save_to_disk_single_format').upper())
|
||||||
@ -1055,6 +1059,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
if hasattr(d, 'directories'):
|
if hasattr(d, 'directories'):
|
||||||
set_sidebar_directories(d.directories)
|
set_sidebar_directories(d.directories)
|
||||||
self.library_view.model().read_config()
|
self.library_view.model().read_config()
|
||||||
|
self.library_view.columns_sorted()
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
@ -1215,8 +1220,13 @@ in which you want to store your books files. Any existing books will be automati
|
|||||||
self.device_manager.keep_going = False
|
self.device_manager.keep_going = False
|
||||||
self.cover_cache.stop()
|
self.cover_cache.stop()
|
||||||
self.hide()
|
self.hide()
|
||||||
time.sleep(2)
|
|
||||||
self.cover_cache.terminate()
|
self.cover_cache.terminate()
|
||||||
|
try:
|
||||||
|
if self.server is not None:
|
||||||
|
self.server.exit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
time.sleep(2)
|
||||||
e.accept()
|
e.accept()
|
||||||
|
|
||||||
def update_found(self, version):
|
def update_found(self, version):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user