mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
c04b7dd482
@ -574,3 +574,17 @@ allow_template_database_functions_in_composites = False
|
|||||||
# for https://whatever URLs. %u is replaced by the URL to be opened. The scheme
|
# for https://whatever URLs. %u is replaced by the URL to be opened. The scheme
|
||||||
# takes a glob pattern allowing a single entry to match multiple URL types.
|
# takes a glob pattern allowing a single entry to match multiple URL types.
|
||||||
openers_by_scheme = {}
|
openers_by_scheme = {}
|
||||||
|
|
||||||
|
#: Change standard column heading text to some value
|
||||||
|
# Use the dictionary below to change a column heading. The format of the
|
||||||
|
# dictionary is
|
||||||
|
# {lookup_name: new_heading, ...}
|
||||||
|
# The new_heading must be unique: no two columns can have the same heading.
|
||||||
|
# This tweak works only with standard columns: it cannot be used to change
|
||||||
|
# the heading for a custom column. If a custom column has the same heading as
|
||||||
|
# one provided here then a number will appended to the custom column's heading
|
||||||
|
# to make it unique.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# alternate_column_headings = {'authors':'Writers', 'size':'MBytes'}
|
||||||
|
alternate_column_headings = {}
|
||||||
|
@ -38,7 +38,7 @@ from calibre.utils.date import (
|
|||||||
UNDEFINED_DATE, as_local_time, dt_factory, is_date_undefined, qt_to_dt,
|
UNDEFINED_DATE, as_local_time, dt_factory, is_date_undefined, qt_to_dt,
|
||||||
)
|
)
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
from calibre.utils.localization import calibre_langcode_to_name, ngettext
|
from calibre.utils.localization import calibre_langcode_to_name
|
||||||
from calibre.utils.resources import get_path as P
|
from calibre.utils.resources import get_path as P
|
||||||
from calibre.utils.search_query_parser import ParseException, SearchQueryParser
|
from calibre.utils.search_query_parser import ParseException, SearchQueryParser
|
||||||
from polyglot.builtins import iteritems, itervalues, string_or_bytes
|
from polyglot.builtins import iteritems, itervalues, string_or_bytes
|
||||||
@ -192,20 +192,12 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.bi_font = QFont(self.bold_font)
|
self.bi_font = QFont(self.bold_font)
|
||||||
self.bi_font.setItalic(True)
|
self.bi_font.setItalic(True)
|
||||||
self.styled_columns = {}
|
self.styled_columns = {}
|
||||||
self.orig_headers = {
|
possible_columns = ('title', 'ondevice', 'authors', 'size', 'timestamp',
|
||||||
'title' : _("Title"),
|
'pubdate', 'rating', 'publisher', 'tags', 'series',
|
||||||
'ondevice' : _("On Device"),
|
'last_modified', 'languages', 'formats', 'id', 'path')
|
||||||
'authors' : _("Author(s)"),
|
from calibre.library.field_metadata import FieldMetadata
|
||||||
'size' : _("Size (MB)"),
|
fm = FieldMetadata()
|
||||||
'timestamp' : _("Date"),
|
self.orig_headers = {k: fm[k]['name'] for k in possible_columns}
|
||||||
'pubdate' : _('Published'),
|
|
||||||
'rating' : _('Rating'),
|
|
||||||
'publisher' : _("Publisher"),
|
|
||||||
'tags' : _("Tags"),
|
|
||||||
'series' : ngettext("Series", 'Series', 1),
|
|
||||||
'last_modified' : _('Modified'),
|
|
||||||
'languages' : _('Languages'),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.db = None
|
self.db = None
|
||||||
|
|
||||||
@ -829,6 +821,10 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
def renderer(field, decorator=False):
|
def renderer(field, decorator=False):
|
||||||
idfunc = self.db.id
|
idfunc = self.db.id
|
||||||
|
if field == 'id':
|
||||||
|
def func(idx):
|
||||||
|
return idfunc(idx)
|
||||||
|
return func
|
||||||
fffunc = self.db.new_api.fast_field_for
|
fffunc = self.db.new_api.fast_field_for
|
||||||
field_obj = self.db.new_api.fields[field]
|
field_obj = self.db.new_api.fields[field]
|
||||||
m = field_obj.metadata.copy()
|
m = field_obj.metadata.copy()
|
||||||
@ -953,7 +949,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
self.dc = {f:renderer(f) for f in 'title authors size timestamp pubdate last_modified rating publisher tags series ondevice languages'.split()}
|
self.dc = {f:renderer(f) for f in self.orig_headers.keys()}
|
||||||
self.dc_decorator = {f:renderer(f, True) for f in ('ondevice',)}
|
self.dc_decorator = {f:renderer(f, True) for f in ('ondevice',)}
|
||||||
|
|
||||||
for col in self.custom_columns:
|
for col in self.custom_columns:
|
||||||
|
@ -808,8 +808,8 @@ class BooksView(QTableView): # {{{
|
|||||||
state = {}
|
state = {}
|
||||||
state['hidden_columns'] = [cm[i] for i in range(h.count())
|
state['hidden_columns'] = [cm[i] for i in range(h.count())
|
||||||
if h.isSectionHidden(i) and cm[i] != 'ondevice']
|
if h.isSectionHidden(i) and cm[i] != 'ondevice']
|
||||||
state['last_modified_injected'] = True
|
for f in ('last_modified', 'languages', 'formats', 'id', 'path'):
|
||||||
state['languages_injected'] = True
|
state[f+'_injected'] = True
|
||||||
state['sort_history'] = \
|
state['sort_history'] = \
|
||||||
self.cleanup_sort_history(self.model().sort_history, ignore_column_map=self.is_library_view)
|
self.cleanup_sort_history(self.model().sort_history, ignore_column_map=self.is_library_view)
|
||||||
state['column_positions'] = {}
|
state['column_positions'] = {}
|
||||||
@ -923,7 +923,7 @@ class BooksView(QTableView): # {{{
|
|||||||
|
|
||||||
def get_default_state(self):
|
def get_default_state(self):
|
||||||
old_state = {
|
old_state = {
|
||||||
'hidden_columns': ['last_modified', 'languages'],
|
'hidden_columns': ['last_modified', 'languages', 'formats', 'id', 'path'],
|
||||||
'sort_history':[DEFAULT_SORT],
|
'sort_history':[DEFAULT_SORT],
|
||||||
'column_positions': {},
|
'column_positions': {},
|
||||||
'column_sizes': {},
|
'column_sizes': {},
|
||||||
@ -931,9 +931,9 @@ class BooksView(QTableView): # {{{
|
|||||||
'size':'center',
|
'size':'center',
|
||||||
'timestamp':'center',
|
'timestamp':'center',
|
||||||
'pubdate':'center'},
|
'pubdate':'center'},
|
||||||
'last_modified_injected': True,
|
|
||||||
'languages_injected': True,
|
|
||||||
}
|
}
|
||||||
|
for f in ('last_modified', 'languages', 'formats', 'id', 'path'):
|
||||||
|
old_state[f+'_injected'] = True
|
||||||
h = self.column_header
|
h = self.column_header
|
||||||
cm = self.column_map
|
cm = self.column_map
|
||||||
for i in range(h.count()):
|
for i in range(h.count()):
|
||||||
@ -965,18 +965,14 @@ class BooksView(QTableView): # {{{
|
|||||||
db.new_api.set_pref(name, ans)
|
db.new_api.set_pref(name, ans)
|
||||||
else:
|
else:
|
||||||
injected = False
|
injected = False
|
||||||
if not ans.get('last_modified_injected', False):
|
for f in ('last_modified', 'languages', 'formats', 'id', 'path'):
|
||||||
injected = True
|
if not ans.get(f+'_injected', False):
|
||||||
ans['last_modified_injected'] = True
|
print('injecting', f)
|
||||||
hc = ans.get('hidden_columns', [])
|
injected = True
|
||||||
if 'last_modified' not in hc:
|
ans[f+'_injected'] = True
|
||||||
hc.append('last_modified')
|
hc = ans.get('hidden_columns', [])
|
||||||
if not ans.get('languages_injected', False):
|
if f not in hc:
|
||||||
injected = True
|
hc.append(f)
|
||||||
ans['languages_injected'] = True
|
|
||||||
hc = ans.get('hidden_columns', [])
|
|
||||||
if 'languages' not in hc:
|
|
||||||
hc.append('languages')
|
|
||||||
if injected:
|
if injected:
|
||||||
db.new_api.set_pref(name, ans)
|
db.new_api.set_pref(name, ans)
|
||||||
return ans
|
return ans
|
||||||
|
@ -5,6 +5,7 @@ Created on 25 May 2010
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
|
import sys
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from calibre.utils.config_base import tweaks
|
from calibre.utils.config_base import tweaks
|
||||||
@ -197,7 +198,7 @@ def _builtin_field_metadata():
|
|||||||
'datatype':'int',
|
'datatype':'int',
|
||||||
'is_multiple':{},
|
'is_multiple':{},
|
||||||
'kind':'field',
|
'kind':'field',
|
||||||
'name':None,
|
'name': _('Id'),
|
||||||
'search_terms':['id'],
|
'search_terms':['id'],
|
||||||
'is_custom':False,
|
'is_custom':False,
|
||||||
'is_category':False,
|
'is_category':False,
|
||||||
@ -411,6 +412,15 @@ class FieldMetadata:
|
|||||||
self._tb_cats[k]['display'] = {}
|
self._tb_cats[k]['display'] = {}
|
||||||
self._tb_cats[k]['is_editable'] = True
|
self._tb_cats[k]['is_editable'] = True
|
||||||
self._add_search_terms_to_map(k, v['search_terms'])
|
self._add_search_terms_to_map(k, v['search_terms'])
|
||||||
|
alternate_headings = tweaks.get('alternate_column_headings', {})
|
||||||
|
if alternate_headings:
|
||||||
|
existing_headings = {k['name'] for k in self._tb_cats.values() if k['name']}
|
||||||
|
for k,v in alternate_headings.items():
|
||||||
|
if k in self._tb_cats.keys():
|
||||||
|
v = self.get_unique_field_heading(v)
|
||||||
|
existing_headings.discard(self._tb_cats[k]['name'])
|
||||||
|
existing_headings.add(v)
|
||||||
|
self._tb_cats[k]['name'] = v
|
||||||
self._tb_cats['timestamp']['display'] = {
|
self._tb_cats['timestamp']['display'] = {
|
||||||
'date_format': tweaks['gui_timestamp_display_format']}
|
'date_format': tweaks['gui_timestamp_display_format']}
|
||||||
self._tb_cats['pubdate']['display'] = {
|
self._tb_cats['pubdate']['display'] = {
|
||||||
@ -454,6 +464,10 @@ class FieldMetadata:
|
|||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def set_field_heading(self, key, heading):
|
||||||
|
if key in self._tb_cats.keys():
|
||||||
|
self._tb_cats[key]['name'] = heading
|
||||||
|
|
||||||
def sortable_field_keys(self):
|
def sortable_field_keys(self):
|
||||||
return [k for k in self._tb_cats.keys()
|
return [k for k in self._tb_cats.keys()
|
||||||
if self._tb_cats[k]['kind']=='field' and
|
if self._tb_cats[k]['kind']=='field' and
|
||||||
@ -560,6 +574,18 @@ class FieldMetadata:
|
|||||||
l[k] = self._tb_cats[k]
|
l[k] = self._tb_cats[k]
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
def get_unique_field_heading(self, name):
|
||||||
|
# Verify column heading is unique. Can only happen if the tweak is set
|
||||||
|
if tweaks.get('alternate_column_headings', {}):
|
||||||
|
existing_names = {icu_lower(c['name']) for c in self._tb_cats.values() if c['name'] is not None}
|
||||||
|
t = icu_lower(name)
|
||||||
|
if t in existing_names:
|
||||||
|
for i in range(1, sys.maxsize):
|
||||||
|
if (t + '_' + str(i)) not in existing_names:
|
||||||
|
name = name + '_' + str(i)
|
||||||
|
break
|
||||||
|
return name
|
||||||
|
|
||||||
def add_custom_field(self, label, table, column, datatype, colnum, name,
|
def add_custom_field(self, label, table, column, datatype, colnum, name,
|
||||||
display, is_editable, is_multiple, is_category,
|
display, is_editable, is_multiple, is_category,
|
||||||
is_csp=False):
|
is_csp=False):
|
||||||
@ -568,6 +594,7 @@ class FieldMetadata:
|
|||||||
raise ValueError('Duplicate custom field [%s]'%(label))
|
raise ValueError('Duplicate custom field [%s]'%(label))
|
||||||
if datatype not in self.VALID_DATA_TYPES:
|
if datatype not in self.VALID_DATA_TYPES:
|
||||||
raise ValueError('Unknown datatype %s for field %s'%(datatype, key))
|
raise ValueError('Unknown datatype %s for field %s'%(datatype, key))
|
||||||
|
name = self.get_unique_field_heading(name)
|
||||||
self._tb_cats[key] = {'table':table, 'column':column,
|
self._tb_cats[key] = {'table':table, 'column':column,
|
||||||
'datatype':datatype, 'is_multiple':is_multiple,
|
'datatype':datatype, 'is_multiple':is_multiple,
|
||||||
'kind':'field', 'name':name,
|
'kind':'field', 'name':name,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user