示例#1
0
    def __init__(self, parent, db):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        for val, text in [(0, '')] + [(i, date(2010, i, 1).strftime('%B')) for i in xrange(1, 13)]:
            self.date_month.addItem(text, val)
        for val, text in [('today', _('Today')), ('yesterday', _('Yesterday')), ('thismonth', _('This month'))]:
            self.date_human.addItem(text, val)
        self.date_year.setValue(now().year)
        self.date_day.setSpecialValueText(u' \xa0')
        vals = [((v['search_terms'] or [k])[0], v['name'] or k) for k, v in db.field_metadata.iteritems() if v.get('datatype', None) == 'datetime']
        for k, v in sorted(vals, key=lambda (k, v): sort_key(v)):
            self.date_field.addItem(v, k)

        self.date_year.valueChanged.connect(lambda : self.sel_date.setChecked(True))
        self.date_month.currentIndexChanged.connect(lambda : self.sel_date.setChecked(True))
        self.date_day.valueChanged.connect(lambda : self.sel_date.setChecked(True))
        self.date_daysago.valueChanged.connect(lambda : self.sel_daysago.setChecked(True))
        self.date_human.currentIndexChanged.connect(lambda : self.sel_human.setChecked(True))
        init_dateop(self.dateop_date)
        self.sel_date.setChecked(True)
        self.mc = ''
        searchables = sorted(db.field_metadata.searchable_fields(),
                             key=lambda x: sort_key(x if x[0] != '#' else x[1:]))
        self.general_combo.addItems(searchables)

        all_authors = db.all_authors()
        all_authors.sort(key=lambda x : sort_key(x[1]))
        self.authors_box.setEditText('')
        self.authors_box.set_separator('&')
        self.authors_box.set_space_before_sep(True)
        self.authors_box.set_add_separator(tweaks['authors_completer_append_separator'])
        self.authors_box.update_items_cache(db.all_author_names())

        all_series = db.all_series()
        all_series.sort(key=lambda x : sort_key(x[1]))
        self.series_box.set_separator(None)
        self.series_box.update_items_cache([x[1] for x in all_series])
        self.series_box.show_initial_value('')

        all_tags = db.all_tags()
        self.tags_box.update_items_cache(all_tags)

        self.box_last_values = copy.deepcopy(box_values)
        if self.box_last_values:
            for k,v in self.box_last_values.items():
                if k == 'general_index':
                    continue
                getattr(self, k).setText(v)
            self.general_combo.setCurrentIndex(
                    self.general_combo.findText(self.box_last_values['general_index']))

        self.clear_button.clicked.connect(self.clear_button_pushed)

        current_tab = gprefs.get('advanced search dialog current tab', 0)
        self.tabWidget.setCurrentIndex(current_tab)
        if current_tab == 1:
            self.matchkind.setCurrentIndex(last_matchkind)

        self.tabWidget.currentChanged[int].connect(self.tab_changed)
        self.tab_changed(current_tab)
示例#2
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
示例#3
0
 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
示例#4
0
文件: ajax.py 项目: davidfor/calibre
def categories(ctx, rd, library_id):
    '''
    Return the list of top-level categories as a list of dictionaries. Each
    dictionary is of the form::
        {
        'name': Display Name,
        'url':URL that gives the JSON object corresponding to all entries in this category,
        'icon': URL to icon of this category,
        'is_category': False for the All Books and Newest categories, True for everything else
        }

    '''
    db = get_db(ctx, rd, library_id)
    with db.safe_read_lock:
        ans = {}
        categories = ctx.get_categories(rd, db)
        category_meta = db.field_metadata
        library_id = db.server_library_id

        def getter(x):
            return category_meta[x]['name']

        displayed_custom_fields = custom_fields_to_display(db)

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue
            display_name = meta['name']
            if category.startswith('@'):
                category = category.partition('.')[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{'url':k, 'name':v[0], 'icon':v[1], 'is_category':True}
                for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x['name']))
        for name, url, icon in [
                (_('All books'), 'allbooks', 'book.png'),
                (_('Newest'), 'newest', 'forward.png'),
                ]:
            ans.insert(0, {'name':name, 'url':url, 'icon':icon,
                'is_category':False})

        for c in ans:
            c['url'] = ctx.url_for(globals()['category'], encoded_name=encode_name(c['url']), library_id=library_id)
            c['icon'] = ctx.url_for(get_icon, which=c['icon'])

        return ans
示例#5
0
文件: ajax.py 项目: BobPyron/calibre
    def ajax_categories(self):
        '''
        Return the list of top-level categories as a list of dictionaries. Each
        dictionary is of the form::
            {
            'name': Display Name,
            'url':URL that gives the JSON object corresponding to all entries in this category,
            'icon': URL to icon of this category,
            'is_category': False for the All Books and Newest categories, True for everything else
            }

        '''
        ans = {}
        categories = self.categories_cache()
        category_meta = self.db.field_metadata

        def getter(x):
            return category_meta[x]['name']

        displayed_custom_fields = custom_fields_to_display(self.db)


        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue
            display_name = meta['name']
            if category.startswith('@'):
                category = category.partition('.')[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{'url':k, 'name':v[0], 'icon':v[1], 'is_category':True}
                for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x['name']))
        for name, url, icon in  [
                (_('All books'), 'allbooks', 'book.png'),
                (_('Newest'), 'newest', 'forward.png'),
                ]:
            ans.insert(0, {'name':name, 'url':url, 'icon':icon,
                'is_category':False})

        for c in ans:
            c['url'] = category_url(self.opts.url_prefix, c['url'])
            c['icon'] = icon_url(self.opts.url_prefix, c['icon'])

        return ans
示例#6
0
 def init_langs(self, db):
     if db is not None:
         pmap = {self._lang_map.get(x[1], x[1]):1 for x in
                 db.get_languages_with_ids()}
         all_items = sorted(self._lang_map.itervalues(),
             key=lambda x: (-pmap.get(x, 0), sort_key(x)))
     else:
         all_items = sorted(self._lang_map.itervalues(),
             key=lambda x: sort_key(x))
     self.update_items_cache(all_items)
示例#7
0
def sort_categories(items, sort, first_letter_sort=False):
    if sort == "popularity":
        key = lambda x: (-getattr(x, "count", 0), sort_key(x.sort or x.name))
    elif sort == "rating":
        key = lambda x: (-getattr(x, "avg_rating", 0.0), sort_key(x.sort or x.name))
    else:
        if first_letter_sort:
            key = lambda x: (collation_order(icu_upper(x.sort or x.name or " ")), sort_key(x.sort or x.name))
        else:
            key = lambda x: sort_key(x.sort or x.name)
    items.sort(key=key)
    return items
示例#8
0
文件: ajax.py 项目: JapaChin/calibre
def categories(ctx, rd, library_id):
    """
    Return the list of top-level categories as a list of dictionaries. Each
    dictionary is of the form::
        {
        'name': Display Name,
        'url':URL that gives the JSON object corresponding to all entries in this category,
        'icon': URL to icon of this category,
        'is_category': False for the All Books and Newest categories, True for everything else
        }

    """
    db = get_db(ctx, library_id)
    with db.safe_read_lock:
        ans = {}
        categories = ctx.get_categories(rd, db)
        category_meta = db.field_metadata
        library_id = db.server_library_id

        def getter(x):
            return category_meta[x]["name"]

        displayed_custom_fields = custom_fields_to_display(db)

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ("formats", "identifiers"):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and category not in displayed_custom_fields:
                continue
            display_name = meta["name"]
            if category.startswith("@"):
                category = category.partition(".")[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{"url": k, "name": v[0], "icon": v[1], "is_category": True} for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x["name"]))
        for name, url, icon in [(_("All books"), "allbooks", "book.png"), (_("Newest"), "newest", "forward.png")]:
            ans.insert(0, {"name": name, "url": url, "icon": icon, "is_category": False})

        for c in ans:
            c["url"] = ctx.url_for(globals()["category"], encoded_name=encode_name(c["url"]), library_id=library_id)
            c["icon"] = ctx.url_for(get_icon, which=c["icon"])

        return ans
示例#9
0
def sort_categories(items, sort, first_letter_sort=False):
    if sort == 'popularity':
        key=lambda x:(-getattr(x, 'count', 0), sort_key(x.sort or x.name))
    elif sort == 'rating':
        key=lambda x:(-getattr(x, 'avg_rating', 0.0), sort_key(x.sort or x.name))
    else:
        if first_letter_sort:
            key=lambda x:(collation_order(icu_upper(x.sort or x.name or ' ')),
                          sort_key(x.sort or x.name))
        else:
            key=lambda x:sort_key(x.sort or x.name)
    items.sort(key=key)
    return items
示例#10
0
    def __init__(self, name, table, bools_are_tristate, get_template_functions):
        self.name, self.table = name, table
        dt = self.metadata['datatype']
        self.has_text_data = dt in {'text', 'comments', 'series', 'enumeration'}
        self.table_type = self.table.table_type
        self._sort_key = (sort_key if dt in ('text', 'series', 'enumeration') else IDENTITY)

        # This will be compared to the output of sort_key() which is a
        # bytestring, therefore it is safer to have it be a bytestring.
        # Coercing an empty bytestring to unicode will never fail, but the
        # output of sort_key cannot be coerced to unicode
        self._default_sort_key = b''

        if dt in {'int', 'float', 'rating'}:
            self._default_sort_key = 0
        elif dt == 'bool':
            self._default_sort_key = None
            self._sort_key = bool_sort_key(bools_are_tristate)
        elif dt == 'datetime':
            self._default_sort_key = UNDEFINED_DATE
            if tweaks['sort_dates_using_visible_fields']:
                fmt = None
                if name in {'timestamp', 'pubdate', 'last_modified'}:
                    fmt = tweaks['gui_%s_display_format' % name]
                elif self.metadata['is_custom']:
                    fmt = self.metadata.get('display', {}).get('date_format', None)
                self._sort_key = partial(clean_date_for_sort, fmt=fmt)
        elif dt == 'comments' or name == 'identifiers':
            self._default_sort_key = ''

        if self.name == 'languages':
            self._sort_key = lambda x:sort_key(calibre_langcode_to_name(x))
        self.is_multiple = (bool(self.metadata['is_multiple']) or self.name ==
                'formats')
        self.sort_sort_key = True
        if self.is_multiple and '&' in self.metadata['is_multiple']['list_to_ui']:
            self._sort_key = lambda x: sort_key(author_to_author_sort(x))
            self.sort_sort_key = False
        self.default_value = {} if name == 'identifiers' else () if self.is_multiple else None
        self.category_formatter = unicode_type
        if dt == 'rating':
            if self.metadata['display'].get('allow_half_stars', False):
                self.category_formatter = lambda x: rating_to_stars(x, True)
            else:
                self.category_formatter = rating_to_stars
        elif name == 'languages':
            self.category_formatter = calibre_langcode_to_name
        self.writer = Writer(self)
        self.series_field = None
        self.get_template_functions = get_template_functions
示例#11
0
文件: opds.py 项目: JimmXinu/calibre
def opds(ctx, rd):
    rc = RequestContext(ctx, rd)
    db = rc.db
    try:
        categories = rc.get_categories(report_parse_errors=True)
    except ParseException as p:
        raise HTTPInternalServerError(p.msg)

    category_meta = db.field_metadata
    cats = [
        (_('Newest'), _('Date'), 'Onewest'),
        (_('Title'), _('Title'), 'Otitle'),
    ]

    def getter(x):
        try:
            return category_meta[x]['name'].lower()
        except KeyError:
            return x

    fm = rc.db.field_metadata
    for category in sorted(categories, key=lambda x: sort_key(getter(x))):
        if fm.is_ignorable_field(category) and not rc.ctx.is_field_displayable(category):
            continue
        if len(categories[category]) == 0:
            continue
        if category in ('formats', 'identifiers'):
            continue
        meta = category_meta.get(category, None)
        if meta is None:
            continue
        cats.append((meta['name'], meta['name'], 'N'+category))
    last_modified = db.last_modified()
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
    return TopLevel(last_modified, cats, rc).root
示例#12
0
def opds(ctx, rd):
    rc = RequestContext(ctx, rd)
    db = rc.db
    categories = rc.get_categories()
    category_meta = db.field_metadata
    cats = [
        (_('Newest'), _('Date'), 'Onewest'),
        (_('Title'), _('Title'), 'Otitle'),
    ]

    def getter(x):
        try:
            return category_meta[x]['name'].lower()
        except KeyError:
            return x

    for category in sorted(categories, key=lambda x: sort_key(getter(x))):
        if len(categories[category]) == 0:
            continue
        if category in ('formats', 'identifiers'):
            continue
        meta = category_meta.get(category, None)
        if meta is None:
            continue
        cats.append((meta['name'], meta['name'], 'N'+category))
    last_modified = db.last_modified()
    rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified))
    return TopLevel(last_modified, cats, rc).root
示例#13
0
文件: code.py 项目: rlugojr/calibre
def get_library_init_data(ctx, rd, db, num, sorts, orders):
    ans = {}
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db, rd.query.get('search', ''), num, 0, ','.join(sorts), ','.join(orders))
        except ParseException:
            ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(((
            sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
                                        key=lambda (field, name):sort_key(name))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        mdata = ans['metadata'] = {}
        try:
            extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(','))
        except Exception:
            extra_books = ()
        for coll in (ans['search_result']['book_ids'], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data
    return ans
示例#14
0
 def create_widgets(self, opt):
     val = self.plugin.prefs[opt.name]
     if opt.type == 'number':
         c = QSpinBox if isinstance(opt.default, int) else QDoubleSpinBox
         widget = c(self)
         widget.setValue(val)
     elif opt.type == 'string':
         widget = QLineEdit(self)
         widget.setText(val if val else '')
     elif opt.type == 'bool':
         widget = QCheckBox(opt.label, self)
         widget.setChecked(bool(val))
     elif opt.type == 'choices':
         widget = QComboBox(self)
         items = list(opt.choices.iteritems())
         items.sort(key=lambda (k, v): sort_key(v))
         for key, label in items:
             widget.addItem(label, (key))
         idx = widget.findData((val))
         widget.setCurrentIndex(idx)
     widget.opt = opt
     widget.setToolTip(textwrap.fill(opt.desc))
     self.widgets.append(widget)
     r = self.l.rowCount()
     if opt.type == 'bool':
         self.l.addWidget(widget, r, 0, 1, self.l.columnCount())
     else:
         l = QLabel(opt.label)
         l.setToolTip(widget.toolTip())
         self.memory.append(l)
         l.setBuddy(widget)
         self.l.addWidget(l, r, 0, 1, 1)
         self.l.addWidget(widget, r, 1, 1, 1)
示例#15
0
    def browse_toplevel(self):
        categories = self.categories_cache()
        category_meta = self.db.field_metadata
        cats = [
                (_('Newest'), 'newest', 'forward.png'),
                (_('All books'), 'allbooks', 'book.png'),
                ]

        def getter(x):
            return category_meta[x]['name'].lower()

        displayed_custom_fields = custom_fields_to_display(self.db)
        uc_displayed = set()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if meta['is_custom'] and category not in displayed_custom_fields:
                continue
            # get the icon files
            if category in self.icon_map:
                icon = '_'+quote(self.icon_map[category])
            elif category in category_icon_map:
                icon = category_icon_map[category]
            elif meta['is_custom']:
                icon = category_icon_map['custom:']
            elif meta['kind'] == 'user':
                icon = category_icon_map['user:'******'blank.png'

            if meta['kind'] == 'user':
                dot = category.find('.')
                if dot > 0:
                    cat = category[:dot]
                    if cat not in uc_displayed:
                        cats.append((meta['name'][:dot-1], cat, icon))
                        uc_displayed.add(cat)
                else:
                    cats.append((meta['name'], category, icon))
                    uc_displayed.add(category)
            else:
                cats.append((meta['name'], category, icon))

        cats = [(u'<li><a title="{2} {0}" href="{3}/browse/category/{1}">&nbsp;</a>'
                 u'<img src="{3}{src}" alt="{0}" />'
                 u'<span class="label">{0}</span>'
                 u'</li>')
                .format(xml(x, True), xml(quote(y)), xml(_('Browse books by')),
                    self.opts.url_prefix, src='/browse/icon/'+z)
                for x, y, z in cats]

        main = u'<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'\
                .format(_('Choose a category to browse by:'), u'\n\n'.join(cats))
        return self.browse_template('name').format(title='',
                    script='toplevel();', main=main)
示例#16
0
    def __init__(self, name, table):
        self.name, self.table = name, table
        dt = self.metadata['datatype']
        self.has_text_data = dt in {'text', 'comments', 'series', 'enumeration'}
        self.table_type = self.table.table_type
        self._sort_key = (sort_key if dt in ('text', 'series', 'enumeration') else lambda x: x)

        # This will be compared to the output of sort_key() which is a
        # bytestring, therefore it is safer to have it be a bytestring.
        # Coercing an empty bytestring to unicode will never fail, but the
        # output of sort_key cannot be coerced to unicode
        self._default_sort_key = b''

        if dt in {'int', 'float', 'rating'}:
            self._default_sort_key = 0
        elif dt == 'bool':
            self._default_sort_key = None
        elif dt == 'datetime':
            self._default_sort_key = UNDEFINED_DATE
        if self.name == 'languages':
            self._sort_key = lambda x:sort_key(calibre_langcode_to_name(x))
        self.is_multiple = (bool(self.metadata['is_multiple']) or self.name ==
                'formats')
        self.default_value = {} if name == 'identifiers' else () if self.is_multiple else None
        self.category_formatter = type(u'')
        if dt == 'rating':
            self.category_formatter = lambda x:'\u2605'*int(x/2)
        elif name == 'languages':
            self.category_formatter = calibre_langcode_to_name
        self.writer = Writer(self)
        self.series_field = None
示例#17
0
文件: pretty.py 项目: Gondulf/calibre
    def manifest_key(x):
        mt = x.get('media-type', '')
        href = x.get('href', '')
        ext = href.rpartition('.')[-1].lower()
        cat = 1000
        if mt in OEB_DOCS:
            cat = 0
        elif mt == guess_type('a.ncx'):
            cat = 1
        elif mt in OEB_STYLES:
            cat = 2
        elif mt.startswith('image/'):
            cat = 3
        elif ext in {'otf', 'ttf', 'woff'}:
            cat = 4
        elif mt.startswith('audio/'):
            cat = 5
        elif mt.startswith('video/'):
            cat = 6

        if cat == 0:
            i = spine_ids.get(x.get('id', None), 1000000000)
        else:
            i = sort_key(href)
        return (cat, i)
示例#18
0
文件: widgets.py 项目: GRiker/calibre
 def create_known_type_map(self):
     _ = lambda x: x
     self.known_type_map = {
         "title-page": _("Title Page"),
         "toc": _("Table of Contents"),
         "index": _("Index"),
         "glossary": _("Glossary"),
         "acknowledgements": _("Acknowledgements"),
         "bibliography": _("Bibliography"),
         "colophon": _("Colophon"),
         "copyright-page": _("Copyright page"),
         "dedication": _("Dedication"),
         "epigraph": _("Epigraph"),
         "foreword": _("Foreword"),
         "loi": _("List of Illustrations"),
         "lot": _("List of Tables"),
         "notes:": _("Notes"),
         "preface": _("Preface"),
         "text": _("Text"),
     }
     _ = __builtins__["_"]
     type_map_help = {
         "title-page": _("Page with title, author, publisher, etc."),
         "index": _("Back-of-book style index"),
         "text": _('First "real" page of content'),
     }
     t = _
     all_types = [
         (k, (("%s (%s)" % (t(v), type_map_help[k])) if k in type_map_help else t(v)))
         for k, v in self.known_type_map.iteritems()
     ]
     all_types.sort(key=lambda x: sort_key(x[1]))
     self.all_types = OrderedDict(all_types)
示例#19
0
文件: code.py 项目: jilanfang/calibre
def interface_data(ctx, rd):
    '''
    Return the data needed to create the server main UI

    Optional: ?num=50&sort=timestamp.desc&library_id=<default library>
              &search=''&extra_books=''
    '''
    ans = {
        'username':rd.username,
        'output_format':prefs['output_format'].upper(),
        'input_formats':{x.upper():True for x in available_input_formats()},
        'gui_pubdate_display_format':tweaks['gui_pubdate_display_format'],
        'gui_timestamp_display_format':tweaks['gui_timestamp_display_format'],
        'gui_last_modified_display_format':tweaks['gui_last_modified_display_format'],
        'use_roman_numerals_for_series_number': get_use_roman(),
    }
    ans['library_map'], ans['default_library'] = ctx.library_info(rd)
    ud = {}
    if rd.username:
        # Override session data with stored values for the authenticated user,
        # if any
        ud = ctx.user_manager.get_session_data(rd.username)
        lid = ud.get('library_id')
        if lid and lid in ans['library_map']:
            rd.query.set('library_id', lid)
        usort = ud.get('sort')
        if usort:
            rd.query.set('sort', usort)
    ans['library_id'], db, sorts, orders = get_basic_query_data(ctx, rd)
    ans['user_session_data'] = ud
    try:
        num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db, rd.query.get('search', ''), num, 0, ','.join(sorts), ','.join(orders))
        except ParseException:
            ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(((
            sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
                                        key=lambda (field, name):sort_key(name))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        ans['icon_map'] = icon_map()
        ans['icon_path'] = ctx.url_for('/icon', which='')
        mdata = ans['metadata'] = {}
        try:
            extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(','))
        except Exception:
            extra_books = ()
        for coll in (ans['search_result']['book_ids'], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data

    return ans
示例#20
0
    def initialize_category_lists(self, book_ids):
        self.db_categories = self.db.new_api.get_categories(book_ids=book_ids)
        self.all_items = []
        self.all_items_dict = {}
        for idx,label in enumerate(self.category_labels):
            if idx == 0:
                continue
            for n in self.category_values[idx]():
                t = Item(name=n, label=label, index=len(self.all_items),
                         icon=self.category_icons[idx], exists=True)
                self.all_items.append(t)
                self.all_items_dict[icu_lower(label+':'+n)] = t

        for cat in self.categories:
            for item,l in enumerate(self.categories[cat]):
                key = icu_lower(':'.join([l[1], l[0]]))
                t = self.all_items_dict.get(key, None)
                if l[1] in self.category_labels:
                    if t is None:
                        t = Item(name=l[0], label=l[1], index=len(self.all_items),
                                 icon=self.category_icons[self.category_labels.index(l[1])],
                                 exists=False)
                        self.all_items.append(t)
                        self.all_items_dict[key] = t
                    l[2] = t.index
                else:
                    # remove any references to a category that no longer exists
                    del self.categories[cat][item]

        self.all_items_sorted = sorted(self.all_items, key=lambda x: sort_key(x.name))
示例#21
0
文件: opds.py 项目: JapaChin/calibre
    def opds(self, version=0):
        version = int(version)
        if version not in BASE_HREFS:
            raise cherrypy.HTTPError(404, "Not found")
        categories = self.categories_cache(self.get_opds_allowed_ids_for_version(version))
        category_meta = self.db.field_metadata
        cats = [(_("Newest"), _("Date"), "Onewest"), (_("Title"), _("Title"), "Otitle")]

        def getter(x):
            try:
                return category_meta[x]["name"].lower()
            except KeyError:
                return x

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ("formats", "identifiers"):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and category not in custom_fields_to_display(self.db):
                continue
            cats.append((meta["name"], meta["name"], "N" + category))
        updated = self.db.last_modified()

        cherrypy.response.headers["Last-Modified"] = self.last_modified(updated)
        cherrypy.response.headers["Content-Type"] = "application/atom+xml"

        feed = TopLevel(updated, cats, version)

        return str(feed)
示例#22
0
def opds(ctx, rd):
    rc = RequestContext(ctx, rd)
    db = rc.db
    categories = rc.get_categories()
    category_meta = db.field_metadata
    cats = [(_("Newest"), _("Date"), "Onewest"), (_("Title"), _("Title"), "Otitle")]

    def getter(x):
        try:
            return category_meta[x]["name"].lower()
        except KeyError:
            return x

    for category in sorted(categories, key=lambda x: sort_key(getter(x))):
        if len(categories[category]) == 0:
            continue
        if category in ("formats", "identifiers"):
            continue
        meta = category_meta.get(category, None)
        if meta is None:
            continue
        cats.append((meta["name"], meta["name"], "N" + category))
    last_modified = db.last_modified()
    rd.outheaders["Last-Modified"] = http_date(timestampfromdt(last_modified))
    return TopLevel(last_modified, cats, rc).root
示例#23
0
 def setup_ui(self):
     self.l = l = QVBoxLayout(self)
     self.la = la = QLabel(_(
         'Create rules to convert identifiers into links.'))
     la.setWordWrap(True)
     l.addWidget(la)
     items = []
     for k, lx in msprefs['id_link_rules'].iteritems():
         for n, t in lx:
             items.append((k, n, t))
     items.sort(key=lambda x:sort_key(x[1]))
     self.table = t = QTableWidget(len(items), 3, self)
     t.setHorizontalHeaderLabels([_('Key'), _('Name'), _('Template')])
     for r, (key, val, template) in enumerate(items):
         t.setItem(r, 0, QTableWidgetItem(key))
         t.setItem(r, 1, QTableWidgetItem(val))
         t.setItem(r, 2, QTableWidgetItem(template))
     l.addWidget(t)
     t.horizontalHeader().setSectionResizeMode(2, t.horizontalHeader().Stretch)
     self.cb = b = QPushButton(QIcon(I('plus.png')), _('&Add rule'), self)
     b.clicked.connect(lambda : self.edit_rule())
     self.bb.addButton(b, self.bb.ActionRole)
     self.rb = b = QPushButton(QIcon(I('minus.png')), _('&Remove rule'), self)
     b.clicked.connect(lambda : self.remove_rule())
     self.bb.addButton(b, self.bb.ActionRole)
     self.eb = b = QPushButton(QIcon(I('modified.png')), _('&Edit rule'), self)
     b.clicked.connect(lambda : self.edit_rule(self.table.currentRow()))
     self.bb.addButton(b, self.bb.ActionRole)
     l.addWidget(self.bb)
示例#24
0
 def create_known_type_map(self):
     _ = lambda x: x
     self.known_type_map = {
         'title-page': _('Title Page'),
         'toc': _('Table of Contents'),
         'index': _('Index'),
         'glossary': _('Glossary'),
         'acknowledgements': _('Acknowledgements'),
         'bibliography': _('Bibliography'),
         'colophon': _('Colophon'),
         'copyright-page': _('Copyright page'),
         'dedication': _('Dedication'),
         'epigraph': _('Epigraph'),
         'foreword': _('Foreword'),
         'loi': _('List of Illustrations'),
         'lot': _('List of Tables'),
         'notes': _('Notes'),
         'preface': _('Preface'),
         'text': _('Text'),
     }
     _ = __builtins__['_']
     type_map_help = {
         'title-page': _('Page with title, author, publisher, etc.'),
         'index': _('Back-of-book style index'),
         'text': _('First "real" page of content'),
     }
     t = _
     all_types = [(k, (('%s (%s)' % (t(v), type_map_help[k])) if k in type_map_help else t(v))) for k, v in self.known_type_map.iteritems()]
     all_types.sort(key=lambda x: sort_key(x[1]))
     self.all_types = OrderedDict(all_types)
示例#25
0
 def rebuild(self):
     self.currentChanged.disconnect(self.tab_changed)
     db = self.current_db
     virt_libs = frozenset(db.prefs.get("virtual_libraries", {}))
     hidden = frozenset(db.prefs["virt_libs_hidden"])
     if hidden - virt_libs:
         db.prefs["virt_libs_hidden"] = list(hidden.intersection(virt_libs))
     order = db.prefs["virt_libs_order"]
     while self.count():
         self.removeTab(0)
     current_lib = db.data.get_base_restriction_name()
     current_idx = all_idx = None
     virt_libs = (set(virt_libs) - hidden) | {""}
     order = {x: i for i, x in enumerate(order)}
     for i, vl in enumerate(sorted(virt_libs, key=lambda x: (order.get(x, 0), sort_key(x)))):
         self.addTab(vl or _("All books"))
         self.setTabData(i, vl)
         if vl == current_lib:
             current_idx = i
         if not vl:
             all_idx = i
     self.setCurrentIndex(all_idx if current_idx is None else current_idx)
     self.currentChanged.connect(self.tab_changed)
     try:
         self.tabButton(all_idx, self.RightSide).setVisible(False)
     except AttributeError:
         try:
             self.tabButton(all_idx, self.LeftSide).setVisible(False)
         except AttributeError:
             # On some OS X machines (using native style) the tab button is
             # on the left
             pass
示例#26
0
文件: opds.py 项目: Eksmo/calibre
    def opds(self, version=0):
        version = int(version)
        if version not in BASE_HREFS:
            raise cherrypy.HTTPError(404, 'Not found')
        categories = self.categories_cache(
                self.get_opds_allowed_ids_for_version(version))
        category_meta = self.db.field_metadata
        cats = [
                (_('Newest'), _('Date'), 'Onewest'),
                (_('Title'), _('Title'), 'Otitle'),
                ]
        def getter(x):
            return category_meta[x]['name'].lower()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            cats.append((meta['name'], meta['name'], 'N'+category))
        updated = self.db.last_modified()

        cherrypy.response.headers['Last-Modified'] = self.last_modified(updated)
        cherrypy.response.headers['Content-Type'] = 'application/atom+xml'

        feed = TopLevel(updated, cats, version)

        return str(feed)
示例#27
0
 def fill_applied_items(self):
     if self.current_cat_name:
         self.applied_items = [cat[2] for cat in self.categories.get(self.current_cat_name, [])]
     else:
         self.applied_items = []
     self.applied_items.sort(key=lambda x:sort_key(self.all_items[x].name))
     self.display_filtered_categories(None)
示例#28
0
文件: search.py 项目: 089git/calibre
    def __init__(self, parent, db):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.mc = ''
        searchables = sorted(db.field_metadata.searchable_fields(),
                             key=lambda x: sort_key(x if x[0] != '#' else x[1:]))
        self.general_combo.addItems(searchables)

        all_authors = db.all_authors()
        all_authors.sort(key=lambda x : sort_key(x[1]))
        self.authors_box.setEditText('')
        self.authors_box.set_separator('&')
        self.authors_box.set_space_before_sep(True)
        self.authors_box.set_add_separator(tweaks['authors_completer_append_separator'])
        self.authors_box.update_items_cache(db.all_author_names())

        all_series = db.all_series()
        all_series.sort(key=lambda x : sort_key(x[1]))
        self.series_box.set_separator(None)
        self.series_box.update_items_cache([x[1] for x in all_series])
        self.series_box.show_initial_value('')

        all_tags = db.all_tags()
        self.tags_box.update_items_cache(all_tags)

        self.box_last_values = copy.deepcopy(box_values)
        if self.box_last_values:
            for k,v in self.box_last_values.items():
                if k == 'general_index':
                    continue
                getattr(self, k).setText(v)
            self.general_combo.setCurrentIndex(
                    self.general_combo.findText(self.box_last_values['general_index']))

        self.buttonBox.accepted.connect(self.advanced_search_button_pushed)
        self.tab_2_button_box.accepted.connect(self.accept)
        self.tab_2_button_box.rejected.connect(self.reject)
        self.clear_button.clicked.connect(self.clear_button_pushed)
        self.adv_search_used = False

        current_tab = gprefs.get('advanced search dialog current tab', 0)
        self.tabWidget.setCurrentIndex(current_tab)
        if current_tab == 1:
            self.matchkind.setCurrentIndex(last_matchkind)

        self.tabWidget.currentChanged[int].connect(self.tab_changed)
        self.tab_changed(current_tab)
示例#29
0
    def add_builtin_recipe(self):
        from calibre.web.feeds.recipes.collection import \
            get_builtin_recipe_collection, get_builtin_recipe_by_id
        from PyQt5.Qt import QDialog, QVBoxLayout, QListWidgetItem, \
                QListWidget, QDialogButtonBox, QSize

        d = QDialog(self)
        d.l = QVBoxLayout()
        d.setLayout(d.l)
        d.list = QListWidget(d)
        d.list.doubleClicked.connect(lambda x: d.accept())
        d.l.addWidget(d.list)
        d.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel,
                Qt.Horizontal, d)
        d.bb.accepted.connect(d.accept)
        d.bb.rejected.connect(d.reject)
        d.l.addWidget(d.bb)
        d.setWindowTitle(_('Choose builtin recipe'))
        items = []
        for r in get_builtin_recipe_collection():
            id_ = r.get('id', '')
            title = r.get('title', '')
            lang = r.get('language', '')
            if id_ and title:
                items.append((title + ' [%s]'%lang, id_))

        items.sort(key=lambda x:sort_key(x[0]))
        for title, id_ in items:
            item = QListWidgetItem(title)
            item.setData(Qt.UserRole, id_)
            d.list.addItem(item)

        d.resize(QSize(450, 400))
        ret = d.exec_()
        d.list.doubleClicked.disconnect()
        if ret != d.Accepted:
            return

        items = list(d.list.selectedItems())
        if not items:
            return
        item = items[-1]
        id_ = unicode(item.data(Qt.UserRole) or '')
        title = unicode(item.data(Qt.DisplayRole) or '').rpartition(' [')[0]
        profile = get_builtin_recipe_by_id(id_, download_recipe=True)
        if profile is None:
            raise Exception('Something weird happened')

        if self._model.has_title(title):
            if question_dialog(self, _('Replace recipe?'),
                _('A custom recipe named %s already exists. Do you want to '
                    'replace it?')%title):
                self._model.replace_by_title(title, profile)
            else:
                return
        else:
            self.model.add(title, profile)

        self.clear()
示例#30
0
文件: books.py 项目: BobPyron/calibre
 def none_cmp(xx, yy):
     x = xx[1]
     y = yy[1]
     if x is None and y is None:
         # No sort_key needed here, because defaults are ascii
         return cmp(xx[2], yy[2])
     if x is None:
         return 1
     if y is None:
         return -1
     if isinstance(x, basestring) and isinstance(y, basestring):
         x, y = sort_key(force_unicode(x)), sort_key(force_unicode(y))
     c = cmp(x, y)
     if c != 0:
         return c
     # same as above -- no sort_key needed here
     return cmp(xx[2], yy[2])
示例#31
0
 def sort_language_items_key(self, val):
     idx = self.recently_used.get(val, 100000)
     return (idx, sort_key(val))
示例#32
0
def field_sort_key(y, fm=None):
    m1 = fm[y]
    name = icu_lower(m1['name'])
    n1 = 'zzzzz' + name if m1['datatype'] == 'comments' and m1.get(
        'display', {}).get('interpret_as') != 'short-text' else name
    return sort_key(n1)
示例#33
0
 def initialize_series(self):
     all_series = self.db.all_series()
     all_series.sort(key=lambda x: sort_key(x[1]))
     self.series.set_separator(None)
     self.series.update_items_cache([x[1] for x in all_series])
示例#34
0
def entry_sort_key(entry):
    return sort_key(entry['Name'])
示例#35
0
def interface_data(ctx, rd):
    '''
    Return the data needed to create the server main UI

    Optional: ?num=50&sort=timestamp.desc&library_id=<default library>
              &search=''&extra_books=''
    '''
    ans = {
        'username':rd.username,
        'output_format':prefs['output_format'].upper(),
        'input_formats':{x.upper():True for x in available_input_formats()},
        'gui_pubdate_display_format':tweaks['gui_pubdate_display_format'],
        'gui_timestamp_display_format':tweaks['gui_timestamp_display_format'],
        'gui_last_modified_display_format':tweaks['gui_last_modified_display_format'],
        'use_roman_numerals_for_series_number': get_use_roman(),
        'translations': get_translations(),
        'allow_console_print':getattr(rd.opts, 'allow_console_print', False),
    }
    ans['library_map'], ans['default_library'] = ctx.library_info(rd)
    ud = {}
    if rd.username:
        # Override session data with stored values for the authenticated user,
        # if any
        ud = ctx.user_manager.get_session_data(rd.username)
        lid = ud.get('library_id')
        if lid and lid in ans['library_map']:
            rd.query.set('library_id', lid)
        usort = ud.get('sort')
        if usort:
            rd.query.set('sort', usort)
    ans['library_id'], db, sorts, orders = get_basic_query_data(ctx, rd)
    ans['user_session_data'] = ud
    try:
        num = int(rd.query.get('num', DEFAULT_NUMBER_OF_BOOKS))
    except Exception:
        raise HTTPNotFound('Invalid number of books: %r' % rd.query.get('num'))
    with db.safe_read_lock:
        try:
            ans['search_result'] = search_result(ctx, rd, db, rd.query.get('search', ''), num, 0, ','.join(sorts), ','.join(orders))
        except ParseException:
            ans['search_result'] = search_result(ctx, rd, db, '', num, 0, ','.join(sorts), ','.join(orders))
        sf = db.field_metadata.ui_sortable_field_keys()
        sf.pop('ondevice', None)
        ans['sortable_fields'] = sorted(((
            sanitize_sort_field_name(db.field_metadata, k), v) for k, v in sf.iteritems()),
                                        key=lambda (field, name):sort_key(name))
        ans['field_metadata'] = db.field_metadata.all_metadata()
        ans['icon_map'] = icon_map()
        ans['icon_path'] = ctx.url_for('/icon', which='')
        mdata = ans['metadata'] = {}
        try:
            extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(','))
        except Exception:
            extra_books = ()
        for coll in (ans['search_result']['book_ids'], extra_books):
            for book_id in coll:
                if book_id not in mdata:
                    data = book_as_json(db, book_id)
                    if data is not None:
                        mdata[book_id] = data

    return ans
示例#36
0
    def __init__(self, fm, pref_name, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        if pref_name == 'column_color_rules':
            self.rule_kind = 'color'
            rule_text = _('coloring')
        else:
            self.rule_kind = 'icon'
            rule_text = _('icon')

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(
            _('Create/edit a column {0} rule').format(rule_text))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(
            _('Create a column {0} rule by'
              ' filling in the boxes below'.format(rule_text)))
        l.addWidget(l1, 0, 0, 1, 8)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 8)

        self.l2 = l2 = QLabel(_('Set the'))
        l.addWidget(l2, 2, 0)

        if self.rule_kind == 'color':
            l.addWidget(QLabel(_('color')))
        else:
            self.kind_box = QComboBox(self)
            for tt, t in icon_rule_kinds:
                self.kind_box.addItem(tt, t)
            l.addWidget(self.kind_box, 2, 1)

        self.l3 = l3 = QLabel(_('of the column:'))
        l.addWidget(l3, 2, 2)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 3)

        self.l4 = l4 = QLabel(_('to'))
        l.addWidget(l4, 2, 4)

        if self.rule_kind == 'color':
            self.color_box = QComboBox(self)
            self.color_label = QLabel('Sample text Sample text')
            self.color_label.setTextFormat(Qt.RichText)
            l.addWidget(self.color_box, 2, 5)
            l.addWidget(self.color_label, 2, 6)
            l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
        else:
            self.filename_box = QComboBox()
            self.filename_box.setInsertPolicy(
                self.filename_box.InsertAlphabetically)
            d = os.path.join(config_dir, 'cc_icons')
            self.icon_file_names = []
            if os.path.exists(d):
                for icon_file in os.listdir(d):
                    icon_file = lower(icon_file)
                    if os.path.exists(os.path.join(d, icon_file)):
                        if icon_file.endswith('.png'):
                            self.icon_file_names.append(icon_file)
            self.icon_file_names.sort(key=sort_key)
            self.update_filename_box()

            l.addWidget(self.filename_box, 2, 5)
            self.filename_button = QPushButton(QIcon(I('document_open.png')),
                                               _('&Add icon'))
            l.addWidget(self.filename_button, 2, 6)
            l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7)
            l.setColumnStretch(7, 10)

        self.l5 = l5 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l5, 3, 0, 1, 7)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 8)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                                          _('Add another condition'))
        l.addWidget(b, 5, 0, 1, 8)
        b.clicked.connect(self.add_blank_condition)

        self.l6 = l6 = QLabel(
            _('You can disable a condition by'
              ' blanking all of its boxes'))
        l.addWidget(l6, 6, 0, 1, 8)

        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok
                                        | QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 8)

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        if self.rule_kind == 'color':
            for b in (self.column_box, self.color_box):
                b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
                b.setMinimumContentsLength(15)

        for key in sorted(displayable_columns(fm),
                          key=lambda (k): sort_key(fm[k]['name'])
                          if k != color_row_key else 0):
            if key == color_row_key and self.rule_kind != 'color':
                continue
            name = all_columns_string if key == color_row_key else fm[key][
                'name']
            if name:
                self.column_box.addItem(name, key)
        self.column_box.setCurrentIndex(0)

        if self.rule_kind == 'color':
            self.color_box.addItems(QColor.colorNames())
            self.color_box.setCurrentIndex(0)
            self.update_color_label()
            self.color_box.currentIndexChanged.connect(self.update_color_label)
        else:
            self.filename_button.clicked.connect(self.filename_button_clicked)

        self.resize(self.sizeHint())
示例#37
0
    def browse_toplevel(self):
        categories = self.categories_cache()
        category_meta = self.db.field_metadata
        cats = [
            (_('Newest'), 'newest', 'forward.png'),
            (_('All books'), 'allbooks', 'book.png'),
        ]

        def getter(x):
            return category_meta[x]['name'].lower()

        displayed_custom_fields = custom_fields_to_display(self.db)
        uc_displayed = set()
        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if meta['is_custom'] and category not in displayed_custom_fields:
                continue
            # get the icon files
            if category in category_icon_map:
                icon = category_icon_map[category]
            elif meta['is_custom']:
                icon = category_icon_map['custom:']
            elif meta['kind'] == 'user':
                icon = category_icon_map['user:'******'blank.png'

            if meta['kind'] == 'user':
                dot = category.find('.')
                if dot > 0:
                    cat = category[:dot]
                    if cat not in uc_displayed:
                        cats.append((meta['name'][:dot - 1], cat, icon))
                        uc_displayed.add(cat)
                else:
                    cats.append((meta['name'], category, icon))
                    uc_displayed.add(category)
            else:
                cats.append((meta['name'], category, icon))

        cats = [(
            u'<li><a title="{2} {0}" href="{3}/browse/category/{1}">&nbsp;</a>'
            u'<img src="{3}{src}" alt="{0}" />'
            u'<span class="label">{0}</span>'
            u'</li>').format(xml(x, True),
                             xml(quote(y)),
                             xml(_('Browse books by')),
                             self.opts.url_prefix,
                             src='/browse/icon/' + z) for x, y, z in cats]

        main = u'<div class="toplevel"><h3>{0}</h3><ul>{1}</ul></div>'\
                .format(_('Choose a category to browse by:'), u'\n\n'.join(cats))
        return self.browse_template('name').format(title='',
                                                   script='toplevel();',
                                                   main=main)
示例#38
0
 def __lt__(self, other):
     return sort_key(unicode_type(self.text())) < sort_key(
         unicode_type(other.text()))
示例#39
0
 def __ge__(self, other):
     return sort_key(unicode_type(self.text())) >= sort_key(
         unicode_type(other.text()))
示例#40
0
def icu_collator(s1, s2):
    return cmp(sort_key(force_unicode(s1, 'utf-8')),
               sort_key(force_unicode(s2, 'utf-8')))
示例#41
0
    def genesis(self, gui):
        self.gui = gui
        if not ismacos and not iswindows:
            self.label_widget_style.setVisible(False)
            self.opt_ui_style.setVisible(False)

        db = gui.library_view.model().db

        r = self.register

        try:
            self.icon_theme_title = json.loads(I('icon-theme.json', data=True))['name']
        except Exception:
            self.icon_theme_title = _('Default icons')
        self.icon_theme.setText(_('Icon theme: <b>%s</b>') % self.icon_theme_title)
        self.commit_icon_theme = None
        self.icon_theme_button.clicked.connect(self.choose_icon_theme)
        self.default_author_link = DefaultAuthorLink(self.default_author_link_container)
        self.default_author_link.changed_signal.connect(self.changed_signal)
        r('gui_layout', config, restart_required=True, choices=[(_('Wide'), 'wide'), (_('Narrow'), 'narrow')])
        r('hidpi', gprefs, restart_required=True, choices=[(_('Automatic'), 'auto'), (_('On'), 'on'), (_('Off'), 'off')])
        if ismacos:
            self.opt_hidpi.setVisible(False), self.label_hidpi.setVisible(False)
        r('ui_style', gprefs, restart_required=True, choices=[(_('System default'), 'system'), (_('calibre style'), 'calibre')])
        r('book_list_tooltips', gprefs)
        r('dnd_merge', gprefs)
        r('wrap_toolbar_text', gprefs, restart_required=True)
        r('show_layout_buttons', gprefs, restart_required=True)
        r('row_numbers_in_book_list', gprefs)
        r('tag_browser_old_look', gprefs)
        r('tag_browser_hide_empty_categories', gprefs)
        r('tag_browser_always_autocollapse', gprefs)
        r('tag_browser_show_tooltips', gprefs)
        r('tag_browser_allow_keyboard_focus', gprefs)
        r('bd_show_cover', gprefs)
        r('bd_overlay_cover_size', gprefs)
        r('cover_grid_width', gprefs)
        r('cover_grid_height', gprefs)
        r('cover_grid_cache_size_multiple', gprefs)
        r('cover_grid_disk_cache_size', gprefs)
        r('cover_grid_spacing', gprefs)
        r('cover_grid_show_title', gprefs)
        r('tag_browser_show_counts', gprefs)
        r('tag_browser_item_padding', gprefs)

        r('qv_respects_vls', gprefs)
        r('qv_dclick_changes_column', gprefs)
        r('qv_retkey_changes_column', gprefs)
        r('qv_follows_column', gprefs)

        r('cover_flow_queue_length', config, restart_required=True)
        r('cover_browser_reflections', gprefs)
        r('cover_browser_title_template', db.prefs)
        fm = db.field_metadata
        r('cover_browser_subtitle_field', db.prefs, choices=[(_('No subtitle'), 'none')] + sorted(
            (fm[k].get('name'), k) for k in fm.all_field_keys() if fm[k].get('name')
        ))
        r('emblem_size', gprefs)
        r('emblem_position', gprefs, choices=[
            (_('Left'), 'left'), (_('Top'), 'top'), (_('Right'), 'right'), (_('Bottom'), 'bottom')])
        r('book_list_extra_row_spacing', gprefs)
        r('booklist_grid', gprefs)
        r('book_details_comments_heading_pos', gprefs, choices=[
            (_('Never'), 'hide'), (_('Above text'), 'above'), (_('Beside text'), 'side')])
        self.cover_browser_title_template_button.clicked.connect(self.edit_cb_title_template)
        self.id_links_button.clicked.connect(self.edit_id_link_rules)

        def get_esc_lang(l):
            if l == 'en':
                return 'English'
            return get_language(l)

        lang = get_lang()
        if lang is None or lang not in available_translations():
            lang = 'en'
        items = [(l, get_esc_lang(l)) for l in available_translations()
                 if l != lang]
        if lang != 'en':
            items.append(('en', get_esc_lang('en')))
        items.sort(key=lambda x: x[1].lower())
        choices = [(y, x) for x, y in items]
        # Default language is the autodetected one
        choices = [(get_language(lang), lang)] + choices
        r('language', prefs, choices=choices, restart_required=True)

        r('show_avg_rating', config)
        r('disable_animations', config)
        r('systray_icon', config, restart_required=True)
        r('show_splash_screen', gprefs)
        r('disable_tray_notification', config)
        r('use_roman_numerals_for_series_number', config)
        r('separate_cover_flow', config, restart_required=True)
        r('cb_fullscreen', gprefs)
        r('cb_preserve_aspect_ratio', gprefs)

        choices = [(_('Off'), 'off'), (_('Small'), 'small'),
            (_('Medium'), 'medium'), (_('Large'), 'large')]
        r('toolbar_icon_size', gprefs, choices=choices)

        choices = [(_('If there is enough room'), 'auto'), (_('Always'), 'always'),
            (_('Never'), 'never')]
        r('toolbar_text', gprefs, choices=choices)

        choices = [(_('Disabled'), 'disable'), (_('By first letter'), 'first letter'),
                   (_('Partitioned'), 'partition')]
        r('tags_browser_partition_method', gprefs, choices=choices)
        r('tags_browser_collapse_at', gprefs)
        r('tags_browser_collapse_fl_at', gprefs)

        choices = {k for k in db.field_metadata.all_field_keys()
                if (db.field_metadata[k]['is_category'] and (
                    db.field_metadata[k]['datatype'] in ['text', 'series', 'enumeration'
                    ]) and not db.field_metadata[k]['display'].get('is_names', False)) or (
                    db.field_metadata[k]['datatype'] in ['composite'
                    ] and db.field_metadata[k]['display'].get('make_category', False))}
        choices |= {'search'}
        r('tag_browser_dont_collapse', gprefs, setting=CommaSeparatedList,
          choices=sorted(choices, key=sort_key))

        choices -= {'authors', 'publisher', 'formats', 'news', 'identifiers'}
        self.opt_categories_using_hierarchy.update_items_cache(choices)
        r('categories_using_hierarchy', db.prefs, setting=CommaSeparatedList,
          choices=sorted(choices, key=sort_key))

        fm = db.field_metadata
        choices = sorted(((fm[k]['name'], k) for k in fm.displayable_field_keys() if fm[k]['name']),
                         key=lambda x:sort_key(x[0]))
        r('field_under_covers_in_grid', db.prefs, choices=choices)

        self.current_font = self.initial_font = None
        self.change_font_button.clicked.connect(self.change_font)

        self.display_model = DisplayedFields(self.gui.current_db,
                self.field_display_order)
        self.display_model.dataChanged.connect(self.changed_signal)
        self.field_display_order.setModel(self.display_model)
        connect_lambda(self.df_up_button.clicked, self,
                lambda self: move_field_up(self.field_display_order, self.display_model))
        connect_lambda(self.df_down_button.clicked, self,
                lambda self: move_field_down(self.field_display_order, self.display_model))

        self.qv_display_model = QVDisplayedFields(self.gui.current_db,
                self.qv_display_order)
        self.qv_display_model.dataChanged.connect(self.changed_signal)
        self.qv_display_order.setModel(self.qv_display_model)
        connect_lambda(self.qv_up_button.clicked, self,
                lambda self: move_field_up(self.qv_display_order, self.qv_display_model))
        connect_lambda(self.qv_down_button.clicked, self,
                lambda self: move_field_down(self.qv_display_order, self.qv_display_model))

        self.edit_rules = EditRules(self.tabWidget)
        self.edit_rules.changed.connect(self.changed_signal)
        self.tabWidget.addTab(self.edit_rules,
                QIcon(I('format-fill-color.png')), _('Column &coloring'))

        self.icon_rules = EditRules(self.tabWidget)
        self.icon_rules.changed.connect(self.changed_signal)
        self.tabWidget.addTab(self.icon_rules,
                QIcon(I('icon_choose.png')), _('Column &icons'))

        self.grid_rules = EditRules(self.emblems_tab)
        self.grid_rules.changed.connect(self.changed_signal)
        self.emblems_tab.setLayout(QVBoxLayout())
        self.emblems_tab.layout().addWidget(self.grid_rules)

        self.tabWidget.setCurrentIndex(0)
        keys = [QKeySequence('F11', QKeySequence.SequenceFormat.PortableText), QKeySequence(
            'Ctrl+Shift+F', QKeySequence.SequenceFormat.PortableText)]
        keys = [unicode_type(x.toString(QKeySequence.SequenceFormat.NativeText)) for x in keys]
        self.fs_help_msg.setText(unicode_type(self.fs_help_msg.text())%(
            _(' or ').join(keys)))
        self.size_calculated.connect(self.update_cg_cache_size, type=Qt.ConnectionType.QueuedConnection)
        self.tabWidget.currentChanged.connect(self.tab_changed)

        l = self.cg_background_box.layout()
        self.cg_bg_widget = w = Background(self)
        l.addWidget(w, 0, 0, 3, 1)
        self.cover_grid_color_button = b = QPushButton(_('Change &color'), self)
        b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
        l.addWidget(b, 0, 1)
        b.clicked.connect(self.change_cover_grid_color)
        self.cover_grid_texture_button = b = QPushButton(_('Change &background image'), self)
        b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
        l.addWidget(b, 1, 1)
        b.clicked.connect(self.change_cover_grid_texture)
        self.cover_grid_default_appearance_button = b = QPushButton(_('Restore &default appearance'), self)
        b.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
        l.addWidget(b, 2, 1)
        b.clicked.connect(self.restore_cover_grid_appearance)
        self.cover_grid_empty_cache.clicked.connect(self.empty_cache)
        self.cover_grid_open_cache.clicked.connect(self.open_cg_cache)
        connect_lambda(self.cover_grid_smaller_cover.clicked, self, lambda self: self.resize_cover(True))
        connect_lambda(self.cover_grid_larger_cover.clicked, self, lambda self: self.resize_cover(False))
        self.cover_grid_reset_size.clicked.connect(self.cg_reset_size)
        self.opt_cover_grid_disk_cache_size.setMinimum(self.gui.grid_view.thumbnail_cache.min_disk_cache)
        self.opt_cover_grid_disk_cache_size.setMaximum(self.gui.grid_view.thumbnail_cache.min_disk_cache * 100)
        self.opt_cover_grid_width.valueChanged.connect(self.update_aspect_ratio)
        self.opt_cover_grid_height.valueChanged.connect(self.update_aspect_ratio)
        self.opt_book_details_css.textChanged.connect(self.changed_signal)
        from calibre.gui2.tweak_book.editor.text import get_highlighter, get_theme
        self.css_highlighter = get_highlighter('css')()
        self.css_highlighter.apply_theme(get_theme(None))
        self.css_highlighter.set_document(self.opt_book_details_css.document())
示例#42
0
    def __init__(self,
                 parent,
                 text,
                 mi=None,
                 fm=None,
                 color_field=None,
                 icon_field_key=None,
                 icon_rule_kind=None,
                 doing_emblem=False,
                 text_is_placeholder=False,
                 dialog_is_st_editor=False,
                 global_vars=None,
                 all_functions=None,
                 builtin_functions=None):
        QDialog.__init__(self, parent)
        Ui_TemplateDialog.__init__(self)
        self.setupUi(self)

        self.coloring = color_field is not None
        self.iconing = icon_field_key is not None
        self.embleming = doing_emblem
        self.dialog_is_st_editor = dialog_is_st_editor
        if global_vars is None:
            self.global_vars = {}
        else:
            self.global_vars = global_vars

        cols = []
        self.fm = fm
        if fm is not None:
            for key in sorted(
                    displayable_columns(fm),
                    key=lambda k: sort_key(fm[k]['name']
                                           if k != color_row_key else 0)):
                if key == color_row_key and not self.coloring:
                    continue
                from calibre.gui2.preferences.coloring import all_columns_string
                name = all_columns_string if key == color_row_key else fm[key][
                    'name']
                if name:
                    cols.append((name, key))

        self.color_layout.setVisible(False)
        self.icon_layout.setVisible(False)

        if self.coloring:
            self.color_layout.setVisible(True)
            for n1, k1 in cols:
                self.colored_field.addItem(
                    n1 + (' (' + k1 + ')' if k1 != color_row_key else ''), k1)
            self.colored_field.setCurrentIndex(
                self.colored_field.findData(color_field))
        elif self.iconing or self.embleming:
            self.icon_layout.setVisible(True)
            if self.embleming:
                self.icon_kind_label.setVisible(False)
                self.icon_kind.setVisible(False)
                self.icon_chooser_label.setVisible(False)
                self.icon_field.setVisible(False)

            for n1, k1 in cols:
                self.icon_field.addItem('{} ({})'.format(n1, k1), k1)
            self.icon_file_names = []
            d = os.path.join(config_dir, 'cc_icons')
            if os.path.exists(d):
                for icon_file in os.listdir(d):
                    icon_file = icu_lower(icon_file)
                    if os.path.exists(os.path.join(d, icon_file)):
                        if icon_file.endswith('.png'):
                            self.icon_file_names.append(icon_file)
            self.icon_file_names.sort(key=sort_key)
            self.update_filename_box()

            if self.iconing:
                dex = 0
                from calibre.gui2.preferences.coloring import icon_rule_kinds
                for i, tup in enumerate(icon_rule_kinds):
                    txt, val = tup
                    self.icon_kind.addItem(txt, userData=(val))
                    if val == icon_rule_kind:
                        dex = i
                self.icon_kind.setCurrentIndex(dex)
                self.icon_field.setCurrentIndex(
                    self.icon_field.findData(icon_field_key))

        self.setup_saved_template_editor(not dialog_is_st_editor,
                                         dialog_is_st_editor)
        # Remove help icon on title bar
        icon = self.windowIcon()
        self.setWindowFlags(self.windowFlags()
                            & (~Qt.WindowType.WindowContextHelpButtonHint))
        self.setWindowIcon(icon)

        self.all_functions = all_functions if all_functions else formatter_functions(
        ).get_functions()
        self.builtins = (builtin_functions if builtin_functions else
                         formatter_functions().get_builtins_and_aliases())

        # Set up the display table
        self.table_column_widths = None
        try:
            self.table_column_widths = \
                        gprefs.get('template_editor_table_widths', None)
        except:
            pass
        self.set_mi(mi, fm)

        self.last_text = ''
        self.highlighter = TemplateHighlighter(self.textbox.document(),
                                               builtin_functions=self.builtins)
        self.textbox.cursorPositionChanged.connect(self.text_cursor_changed)
        self.textbox.textChanged.connect(self.textbox_changed)
        self.textbox.setFont(self.get_current_font())

        self.textbox.setTabStopWidth(10)
        self.source_code.setTabStopWidth(10)
        self.documentation.setReadOnly(True)
        self.source_code.setReadOnly(True)

        if text is not None:
            if text_is_placeholder:
                self.textbox.setPlaceholderText(text)
                self.textbox.clear()
                text = ''
            else:
                self.textbox.setPlainText(text)
        else:
            text = ''
        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(
            _('&OK'))
        self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(
            _('&Cancel'))

        self.color_copy_button.clicked.connect(self.color_to_clipboard)
        self.filename_button.clicked.connect(self.filename_button_clicked)
        self.icon_copy_button.clicked.connect(self.icon_to_clipboard)

        try:
            with open(P('template-functions.json'), 'rb') as f:
                self.builtin_source_dict = json.load(f, encoding='utf-8')
        except:
            self.builtin_source_dict = {}

        func_names = sorted(self.all_functions)
        self.function.clear()
        self.function.addItem('')
        for f in func_names:
            self.function.addItem(
                '{}  --  {}'.format(
                    f, self.function_type_string(f, longform=False)), f)
        self.function.setCurrentIndex(0)
        self.function.currentIndexChanged.connect(self.function_changed)
        self.rule = (None, '')

        tt = _('Template language tutorial')
        self.template_tutorial.setText(
            '<a href="%s">%s</a>' % (localize_user_manual_link(
                'https://manual.calibre-ebook.com/template_lang.html'), tt))
        tt = _('Template function reference')
        self.template_func_reference.setText(
            '<a href="%s">%s</a>' % (localize_user_manual_link(
                'https://manual.calibre-ebook.com/generated/en/template_ref.html'
            ), tt))

        s = gprefs.get('template_editor_break_on_print', False)
        self.go_button.setEnabled(s)
        self.remove_all_button.setEnabled(s)
        self.set_all_button.setEnabled(s)
        self.toggle_button.setEnabled(s)
        self.breakpoint_line_box.setEnabled(s)
        self.breakpoint_line_box_label.setEnabled(s)
        self.break_box.setChecked(s)
        self.break_box.stateChanged.connect(self.break_box_changed)
        self.go_button.clicked.connect(self.go_button_pressed)
        self.textbox.setFocus()
        self.set_up_font_boxes()
        self.toggle_button.clicked.connect(self.toggle_button_pressed)
        self.remove_all_button.clicked.connect(self.remove_all_button_pressed)
        self.set_all_button.clicked.connect(self.set_all_button_pressed)

        self.load_button.clicked.connect(self.load_template_from_file)
        self.save_button.clicked.connect(self.save_template)

        self.textbox.setWordWrapMode(QTextOption.WordWrap)
        self.textbox.setContextMenuPolicy(
            Qt.ContextMenuPolicy.CustomContextMenu)
        self.textbox.customContextMenuRequested.connect(self.show_context_menu)
        # Now geometry
        try:
            geom = gprefs.get('template_editor_dialog_geometry', None)
            if geom is not None:
                QApplication.instance().safe_restore_geometry(
                    self, QByteArray(geom))
        except Exception:
            pass
示例#43
0
 def sort_keys_for_books(self, get_metadata, lang_map, all_book_ids):
     return {id_: sort_key(self.get_value_with_cache(id_, get_metadata)) for id_ in
             all_book_ids}
示例#44
0
文件: conf.py 项目: rakyi/calibre
def sort_languages(x):
    from calibre.utils.icu import sort_key
    lc, name = x
    if lc == language:
        return ''
    return sort_key(unicode(name))
示例#45
0
def field_sort_key(y, fm=None):
    m1 = fm[y]
    name = icu_lower(m1['name'])
    n1 = 'zzzzz' + name if m1['datatype'] == 'comments' else name
    return sort_key(n1)
示例#46
0
    def browse_render_details(self, id_):
        try:
            mi = self.db.get_metadata(id_, index_is_id=True)
        except:
            return _('This book has been deleted')
        else:
            args, fmt, fmts, fname = self.browse_get_book_args(
                mi, id_, add_category_links=True)
            args['formats'] = ''
            if fmts:
                ofmts = [u'<a href="{4}/get/{0}/{1}_{2}.{0}" title="{3}">{3}</a>'\
                        .format(fmt, fname, id_, fmt.upper(),
                            self.opts.url_prefix) for fmt in
                        fmts]
                ofmts = ', '.join(ofmts)
                args['formats'] = ofmts
            fields, comments = [], []
            displayed_custom_fields = custom_fields_to_display(self.db)
            for field, m in list(mi.get_all_standard_metadata(False).items()) + \
                    list(mi.get_all_user_metadata(False).items()):
                if m['is_custom'] and field not in displayed_custom_fields:
                    continue
                if m['datatype'] == 'comments' or field == 'comments' or (
                        m['datatype'] == 'composite' and \
                            m['display'].get('contains_html', False)):
                    val = mi.get(field, '')
                    if val and val.strip():
                        comments.append((m['name'], comments_to_html(val)))
                    continue
                if field in ('title', 'formats') or not args.get(field, False) \
                        or not m['name']:
                    continue
                if m['datatype'] == 'rating':
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                            render_rating(mi.get(field)/2.0, self.opts.url_prefix,
                                    prefix=m['name'])[0]
                else:
                    r = u'<strong>%s: </strong>'%xml(m['name']) + \
                                args[field]
                fields.append((m['name'], r))

            fields.sort(key=lambda x: sort_key(x[0]))
            fields = [
                u'<div class="field">{0}</div>'.format(f[1]) for f in fields
            ]
            fields = u'<div class="fields">%s</div>' % ('\n\n'.join(fields))

            comments.sort(key=lambda x: x[0].lower())
            comments = [
                (u'<div class="field"><strong>%s: </strong>'
                 u'<div class="comment">%s</div></div>') % (xml(c[0]), c[1])
                for c in comments
            ]
            comments = u'<div class="comments">%s</div>' % (
                '\n\n'.join(comments))

            return self.browse_details_template.format(id=id_,
                                                       title=xml(
                                                           mi.title, True),
                                                       fields=fields,
                                                       formats=args['formats'],
                                                       comments=comments)
示例#47
0
文件: mobile.py 项目: tokot/calibre
    def mobile(self,
               start='1',
               num='25',
               sort='date',
               search='',
               _=None,
               order='descending'):
        '''
        Serves metadata from the calibre database as XML.

        :param sort: Sort results by ``sort``. Can be one of `title,author,rating`.
        :param search: Filter results by ``search`` query. See :class:`SearchQueryParser` for query syntax
        :param start,num: Return the slice `[start:start+num]` of the sorted and filtered results
        :param _: Firefox seems to sometimes send this when using XMLHttpRequest with no caching
        '''
        try:
            start = int(start)
        except ValueError:
            raise cherrypy.HTTPError(400,
                                     'start: %s is not an integer' % start)
        try:
            num = int(num)
        except ValueError:
            raise cherrypy.HTTPError(400, 'num: %s is not an integer' % num)
        if not search:
            search = ''
        if isbytestring(search):
            search = search.decode('UTF-8')
        ids = self.search_for_books(search)
        FM = self.db.FIELD_MAP
        items = [r for r in iter(self.db) if r[FM['id']] in ids]
        if sort is not None:
            self.sort(items, sort, (order.lower().strip() == 'ascending'))

        CFM = self.db.field_metadata
        CKEYS = [
            key for key in sorted(custom_fields_to_display(self.db),
                                  key=lambda x: sort_key(CFM[x]['name']))
        ]
        # This method uses its own book dict, not the Metadata dict. The loop
        # below could be changed to use db.get_metadata instead of reading
        # info directly from the record made by the view, but it doesn't seem
        # worth it at the moment.
        books = []
        for record in items[(start - 1):(start - 1) + num]:
            book = {
                'formats': record[FM['formats']],
                'size': record[FM['size']]
            }
            if not book['formats']:
                book['formats'] = ''
            if not book['size']:
                book['size'] = 0
            book['size'] = human_readable(book['size'])

            aus = record[FM['authors']] if record[
                FM['authors']] else __builtin__._('Unknown')
            aut_is = CFM['authors']['is_multiple']
            authors = aut_is['list_to_ui'].join(
                [i.replace('|', ',') for i in aus.split(',')])
            book['authors'] = authors
            book['series_index'] = fmt_sidx(float(record[FM['series_index']]))
            book['series'] = record[FM['series']]
            book['tags'] = format_tag_string(record[FM['tags']],
                                             ',',
                                             no_tag_count=True)
            book['title'] = record[FM['title']]
            for x in ('timestamp', 'pubdate'):
                book[x] = strftime('%d %b, %Y', as_local_time(record[FM[x]]))
            book['id'] = record[FM['id']]
            books.append(book)
            for key in CKEYS:

                def concat(name, val):
                    return '%s:#:%s' % (name, unicode(val))

                mi = self.db.get_metadata(record[CFM['id']['rec_index']],
                                          index_is_id=True)
                name, val = mi.format_field(key)
                if not val:
                    continue
                datatype = CFM[key]['datatype']
                if datatype in ['comments']:
                    continue
                if datatype == 'text' and CFM[key]['is_multiple']:
                    book[key] = concat(
                        name,
                        format_tag_string(
                            val,
                            CFM[key]['is_multiple']['ui_to_list'],
                            no_tag_count=True,
                            joinval=CFM[key]['is_multiple']['list_to_ui']))
                else:
                    book[key] = concat(name, val)

        updated = self.db.last_modified()

        cherrypy.response.headers['Content-Type'] = 'text/html; charset=utf-8'
        cherrypy.response.headers['Last-Modified'] = self.last_modified(
            updated)

        url_base = "/mobile?search=" + search + ";order=" + order + ";sort=" + sort + ";num=" + str(
            num)
        ua = cherrypy.request.headers.get('User-Agent', '').strip()
        have_kobo_browser = self.is_kobo_browser(ua)

        raw = html.tostring(build_index(books,
                                        num,
                                        search,
                                        sort,
                                        order,
                                        start,
                                        len(ids),
                                        url_base,
                                        CKEYS,
                                        self.opts.url_prefix,
                                        have_kobo_browser=have_kobo_browser),
                            encoding='utf-8',
                            pretty_print=True)
        # tostring's include_meta_content_type is broken
        raw = raw.replace(
            '<head>', '<head>\n'
            '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
        )
        return raw
示例#48
0
    def create_menubar(self):
        if isosx:
            p, q = self.create_application_menubar()
            q.triggered.connect(self.action_quit.trigger)
            p.triggered.connect(self.action_preferences.trigger)
        f = factory(app_id='com.calibre-ebook.EditBook-%d' % os.getpid())
        b = f.create_window_menubar(self)

        f = b.addMenu(_('&File'))
        f.addAction(self.action_new_file)
        f.addAction(self.action_import_files)
        f.addSeparator()
        f.addAction(self.action_open_book)
        f.addAction(self.action_new_book)
        f.addAction(self.action_import_book)
        f.addAction(self.action_open_book_folder)
        self.recent_books_menu = f.addMenu(_('&Recently opened books'))
        self.update_recent_books()
        f.addSeparator()
        f.addAction(self.action_save)
        f.addAction(self.action_save_copy)
        f.addSeparator()
        f.addAction(self.action_compare_book)
        f.addAction(self.action_quit)

        e = b.addMenu(_('&Edit'))
        e.addAction(self.action_global_undo)
        e.addAction(self.action_global_redo)
        e.addAction(self.action_create_checkpoint)
        e.addSeparator()
        e.addAction(self.action_editor_undo)
        e.addAction(self.action_editor_redo)
        e.addSeparator()
        e.addAction(self.action_editor_cut)
        e.addAction(self.action_editor_copy)
        e.addAction(self.action_editor_paste)
        e.addAction(self.action_insert_char)
        e.addSeparator()
        e.addAction(self.action_quick_edit)
        e.addAction(self.action_preferences)

        e = b.addMenu(_('&Tools'))
        tm = e.addMenu(_('Table of Contents'))
        tm.addAction(self.action_toc)
        tm.addAction(self.action_inline_toc)
        e.addAction(self.action_manage_fonts)
        e.addAction(self.action_embed_fonts)
        e.addAction(self.action_subset_fonts)
        e.addAction(self.action_compress_images)
        e.addAction(self.action_smarten_punctuation)
        e.addAction(self.action_remove_unused_css)
        e.addAction(self.action_transform_styles)
        e.addAction(self.action_fix_html_all)
        e.addAction(self.action_pretty_all)
        e.addAction(self.action_rationalize_folders)
        e.addAction(self.action_add_cover)
        e.addAction(self.action_set_semantics)
        e.addAction(self.action_filter_css)
        e.addAction(self.action_spell_check_book)
        er = e.addMenu(_('External &links'))
        er.addAction(self.action_check_external_links)
        er.addAction(self.action_get_ext_resources)
        e.addAction(self.action_check_book)
        e.addAction(self.action_reports)
        e.addAction(self.action_upgrade_book_internals)

        e = b.addMenu(_('&View'))
        t = e.addMenu(_('Tool&bars'))
        e.addSeparator()
        for name in sorted(actions, key=lambda x: sort_key(actions[x].text())):
            ac = actions[name]
            if name.endswith('-dock'):
                e.addAction(ac)
            elif name.endswith('-bar'):
                t.addAction(ac)
        e.addAction(self.action_browse_images)
        e.addSeparator()
        e.addAction(self.action_close_current_tab)
        e.addAction(self.action_close_all_but_current_tab)

        e = b.addMenu(_('&Search'))
        a = e.addAction
        a(self.action_find)
        e.addSeparator()
        a(self.action_find_next)
        a(self.action_find_previous)
        e.addSeparator()
        a(self.action_replace)
        a(self.action_replace_next)
        a(self.action_replace_previous)
        a(self.action_replace_all)
        e.addSeparator()
        a(self.action_count)
        e.addSeparator()
        a(self.action_mark)
        e.addSeparator()
        a(self.action_go_to_line)
        e.addSeparator()
        a(self.action_saved_searches)
        e.aboutToShow.connect(self.search_menu_about_to_show)
        e.addSeparator()
        a(self.action_text_search)

        if self.plugin_menu_actions:
            e = b.addMenu(_('&Plugins'))
            for ac in sorted(self.plugin_menu_actions,
                             key=lambda x: sort_key(unicode_type(x.text()))):
                e.addAction(ac)

        e = b.addMenu(_('&Help'))
        a = e.addAction
        a(self.action_help)
        a(QIcon(I('donate.png')), _('&Donate to support calibre development'),
          open_donate)
        a(self.action_preferences)
示例#49
0
def categories(ctx, rd, library_id):
    '''
    Return the list of top-level categories as a list of dictionaries. Each
    dictionary is of the form::
        {
        'name': Display Name,
        'url':URL that gives the JSON object corresponding to all entries in this category,
        'icon': URL to icon of this category,
        'is_category': False for the All Books and Newest categories, True for everything else
        }

    '''
    db = get_db(ctx, rd, library_id)
    with db.safe_read_lock:
        ans = {}
        categories = ctx.get_categories(rd, db)
        category_meta = db.field_metadata
        library_id = db.server_library_id

        def getter(x):
            return category_meta[x]['name']

        displayed_custom_fields = custom_fields_to_display(db)

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue
            display_name = meta['name']
            if category.startswith('@'):
                category = category.partition('.')[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{
            'url': k,
            'name': v[0],
            'icon': v[1],
            'is_category': True
        } for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x['name']))
        for name, url, icon in [
            (_('All books'), 'allbooks', 'book.png'),
            (_('Newest'), 'newest', 'forward.png'),
        ]:
            ans.insert(0, {
                'name': name,
                'url': url,
                'icon': icon,
                'is_category': False
            })

        for c in ans:
            c['url'] = ctx.url_for(globals()['category'],
                                   encoded_name=encode_name(c['url']),
                                   library_id=library_id)
            c['icon'] = ctx.url_for(get_icon, which=c['icon'])

        return ans
示例#50
0
def category(ctx, rd, encoded_name, library_id):
    '''
    Return a dictionary describing the category specified by name. The

    Optional: ?num=100&offset=0&sort=name&sort_order=asc

    The dictionary looks like::

        {
            'category_name': Category display name,
            'base_url': Base URL for this category,
            'total_num': Total numberof items in this category,
            'offset': The offset for the items returned in this result,
            'num': The number of items returned in this result,
            'sort': How the returned items are sorted,
            'sort_order': asc or desc
            'subcategories': List of sub categories of this category.
            'items': List of items in this category,
        }

    Each subcategory is a dictionary of the same form as those returned by
    /ajax/categories

    Each  item is a dictionary of the form::

        {
            'name': Display name,
            'average_rating': Average rating for books in this item,
            'count': Number of books in this item,
            'url': URL to get list of books in this item,
            'has_children': If True this item contains sub categories, look
            for an entry corresponding to this item in subcategories int he
            main dictionary,
        }

    :param sort: How to sort the returned items. Choices are: name, rating,
                    popularity
    :param sort_order: asc or desc

    To learn how to create subcategories see
    https://manual.calibre-ebook.com/sub_groups.html
    '''

    db = get_db(ctx, rd, library_id)
    with db.safe_read_lock:
        num, offset = get_pagination(rd.query)
        sort, sort_order = rd.query.get('sort'), rd.query.get('sort_order')
        sort = ensure_val(sort, 'name', 'rating', 'popularity')
        sort_order = ensure_val(sort_order, 'asc', 'desc')
        try:
            dname = decode_name(encoded_name)
        except:
            raise HTTPNotFound('Invalid encoding of category name %r' %
                               encoded_name)
        base_url = ctx.url_for(globals()['category'],
                               encoded_name=encoded_name,
                               library_id=db.server_library_id)

        if dname in ('newest', 'allbooks'):
            sort, sort_order = 'timestamp', 'desc'
            rd.query['sort'], rd.query['sort_order'] = sort, sort_order
            return books_in(ctx, rd, encoded_name, encode_name('0'),
                            library_id)

        fm = db.field_metadata
        categories = ctx.get_categories(rd, db)
        hierarchical_categories = db.pref('categories_using_hierarchy', ())

        subcategory = dname
        toplevel = subcategory.partition('.')[0]
        if toplevel == subcategory:
            subcategory = None
        if toplevel not in categories or toplevel not in fm:
            raise HTTPNotFound('Category %r not found' % toplevel)

        # Find items and sub categories
        subcategories = []
        meta = fm[toplevel]
        item_names = {}
        children = set()

        if meta['kind'] == 'user':
            fullname = ((toplevel + '.' +
                         subcategory) if subcategory is not None else toplevel)
            try:
                # User categories cannot be applied to books, so this is the
                # complete set of items, no need to consider sub categories
                items = categories[fullname]
            except:
                raise HTTPNotFound('User category %r not found' % fullname)

            parts = fullname.split('.')
            for candidate in categories:
                cparts = candidate.split('.')
                if len(cparts) == len(parts) + 1 and cparts[:-1] == parts:
                    subcategories.append({
                        'name': cparts[-1],
                        'url': candidate,
                        'icon': category_icon(toplevel, meta)
                    })

            category_name = toplevel[1:].split('.')
            # When browsing by user categories we ignore hierarchical normal
            # columns, so children can be empty

        elif toplevel in hierarchical_categories:
            items = []

            category_names = [
                x.original_name.split('.') for x in categories[toplevel]
                if '.' in x.original_name
            ]

            if subcategory is None:
                children = set(x[0] for x in category_names)
                category_name = [meta['name']]
                items = [
                    x for x in categories[toplevel]
                    if '.' not in x.original_name
                ]
            else:
                subcategory_parts = subcategory.split('.')[1:]
                category_name = [meta['name']] + subcategory_parts

                lsp = len(subcategory_parts)
                children = set(
                    '.'.join(x) for x in category_names
                    if len(x) == lsp + 1 and x[:lsp] == subcategory_parts)
                items = [
                    x for x in categories[toplevel]
                    if x.original_name in children
                ]
                item_names = {
                    x: x.original_name.rpartition('.')[-1]
                    for x in items
                }
                # Only mark the subcategories that have children themselves as
                # subcategories
                children = set(
                    '.'.join(x[:lsp + 1]) for x in category_names
                    if len(x) > lsp + 1 and x[:lsp] == subcategory_parts)
            subcategories = [{
                'name': x.rpartition('.')[-1],
                'url': toplevel + '.' + x,
                'icon': category_icon(toplevel, meta)
            } for x in children]
        else:
            items = categories[toplevel]
            category_name = meta['name']

        for x in subcategories:
            x['url'] = ctx.url_for(globals()['category'],
                                   encoded_name=encode_name(x['url']),
                                   library_id=db.server_library_id)
            x['icon'] = ctx.url_for(get_icon, which=x['icon'])
            x['is_category'] = True

        sort_keygen = {
            'name': lambda x: sort_key(x.sort if x.sort else x.original_name),
            'popularity': lambda x: x.count,
            'rating': lambda x: x.avg_rating
        }
        items.sort(key=sort_keygen[sort], reverse=sort_order == 'desc')
        total_num = len(items)
        items = items[offset:offset + num]
        items = [{
            'name':
            item_names.get(x, x.original_name),
            'average_rating':
            x.avg_rating,
            'count':
            x.count,
            'url':
            ctx.url_for(
                books_in,
                encoded_category=encode_name(
                    x.category if x.category else toplevel),
                encoded_item=encode_name(
                    x.original_name if x.id is None else unicode(x.id)),
                library_id=db.server_library_id),
            'has_children':
            x.original_name in children,
        } for x in items]

        return {
            'category_name': category_name,
            'base_url': base_url,
            'total_num': total_num,
            'offset': offset,
            'num': len(items),
            'sort': sort,
            'sort_order': sort_order,
            'subcategories': subcategories,
            'items': items,
        }
示例#51
0
 def entry_sort_key(entry):
     return sort_key(entry.get('name') or '')
示例#52
0
    def genesis(self, gui):
        self.gui = gui
        db = gui.library_view.model().db

        r = self.register

        r('gui_layout',
          config,
          restart_required=True,
          choices=[(_('Wide'), 'wide'), (_('Narrow'), 'narrow')])
        r('ui_style',
          gprefs,
          restart_required=True,
          choices=[(_('System default'), 'system'),
                   (_('Calibre style'), 'calibre')])
        r('book_list_tooltips', gprefs)
        r('tag_browser_old_look', gprefs, restart_required=True)
        r('bd_show_cover', gprefs)
        r('bd_overlay_cover_size', gprefs)
        r('cover_grid_width', gprefs)
        r('cover_grid_height', gprefs)
        r('cover_grid_cache_size_multiple', gprefs)
        r('cover_grid_disk_cache_size', gprefs)
        r('cover_grid_spacing', gprefs)
        r('cover_grid_show_title', gprefs)

        r('cover_flow_queue_length', config, restart_required=True)
        r('cover_browser_reflections', gprefs)
        r('show_rating_in_cover_browser', gprefs)
        r('emblem_size', gprefs)
        r('emblem_position',
          gprefs,
          choices=[(_('Left'), 'left'), (_('Top'), 'top'),
                   (_('Right'), 'right'), (_('Bottom'), 'bottom')])
        r('book_list_extra_row_spacing', gprefs)

        def get_esc_lang(l):
            if l == 'en':
                return 'English'
            return get_language(l)

        lang = get_lang()
        if lang is None or lang not in available_translations():
            lang = 'en'
        items = [(l, get_esc_lang(l)) for l in available_translations()
                 if l != lang]
        if lang != 'en':
            items.append(('en', get_esc_lang('en')))
        items.sort(cmp=lambda x, y: cmp(x[1].lower(), y[1].lower()))
        choices = [(y, x) for x, y in items]
        # Default language is the autodetected one
        choices = [(get_language(lang), lang)] + choices
        r('language', prefs, choices=choices, restart_required=True)

        r('show_avg_rating', config)
        r('disable_animations', config)
        r('systray_icon', config, restart_required=True)
        r('show_splash_screen', gprefs)
        r('disable_tray_notification', config)
        r('use_roman_numerals_for_series_number', config)
        r('separate_cover_flow', config, restart_required=True)
        r('cb_fullscreen', gprefs)
        r('cb_preserve_aspect_ratio', gprefs)

        choices = [(_('Off'), 'off'), (_('Small'), 'small'),
                   (_('Medium'), 'medium'), (_('Large'), 'large')]
        r('toolbar_icon_size', gprefs, choices=choices)

        choices = [(_('If there is enough room'), 'auto'),
                   (_('Always'), 'always'), (_('Never'), 'never')]
        r('toolbar_text', gprefs, choices=choices)

        choices = [(_('Disabled'), 'disable'),
                   (_('By first letter'), 'first letter'),
                   (_('Partitioned'), 'partition')]
        r('tags_browser_partition_method', gprefs, choices=choices)
        r('tags_browser_collapse_at', gprefs)
        r('default_author_link', gprefs)
        r('tag_browser_dont_collapse', gprefs, setting=CommaSeparatedList)

        self.search_library_for_author_button.clicked.connect(
            lambda: self.opt_default_author_link.setText('search-calibre'))

        choices = set([
            k for k in db.field_metadata.all_field_keys()
            if (db.field_metadata[k]['is_category'] and
                (db.field_metadata[k]['datatype'] in
                 ['text', 'series', 'enumeration'])
                and not db.field_metadata[k]['display'].get('is_names', False))
            or (db.field_metadata[k]['datatype'] in ['composite'] and
                db.field_metadata[k]['display'].get('make_category', False))
        ])
        choices -= set(
            ['authors', 'publisher', 'formats', 'news', 'identifiers'])
        choices |= set(['search'])
        self.opt_categories_using_hierarchy.update_items_cache(choices)
        r('categories_using_hierarchy',
          db.prefs,
          setting=CommaSeparatedList,
          choices=sorted(list(choices), key=sort_key))

        fm = db.field_metadata
        choices = sorted(
            ((fm[k]['name'], k)
             for k in fm.displayable_field_keys() if fm[k]['name']),
            key=lambda x: sort_key(x[0]))
        r('field_under_covers_in_grid', db.prefs, choices=choices)

        self.current_font = self.initial_font = None
        self.change_font_button.clicked.connect(self.change_font)

        self.display_model = DisplayedFields(self.gui.current_db,
                                             self.field_display_order)
        self.display_model.dataChanged.connect(self.changed_signal)
        self.field_display_order.setModel(self.display_model)
        self.df_up_button.clicked.connect(self.move_df_up)
        self.df_down_button.clicked.connect(self.move_df_down)

        self.edit_rules = EditRules(self.tabWidget)
        self.edit_rules.changed.connect(self.changed_signal)
        self.tabWidget.addTab(self.edit_rules,
                              QIcon(I('format-fill-color.png')),
                              _('Column coloring'))

        self.icon_rules = EditRules(self.tabWidget)
        self.icon_rules.changed.connect(self.changed_signal)
        self.tabWidget.addTab(self.icon_rules, QIcon(I('icon_choose.png')),
                              _('Column icons'))

        self.grid_rules = EditRules(self.emblems_tab)
        self.grid_rules.changed.connect(self.changed_signal)
        self.emblems_tab.setLayout(QVBoxLayout())
        self.emblems_tab.layout().addWidget(self.grid_rules)

        self.tabWidget.setCurrentIndex(0)
        keys = [
            QKeySequence('F11', QKeySequence.PortableText),
            QKeySequence('Ctrl+Shift+F', QKeySequence.PortableText)
        ]
        keys = [unicode(x.toString(QKeySequence.NativeText)) for x in keys]
        self.fs_help_msg.setText(
            unicode(self.fs_help_msg.text()) % (_(' or ').join(keys)))
        self.size_calculated.connect(self.update_cg_cache_size,
                                     type=Qt.QueuedConnection)
        self.tabWidget.currentChanged.connect(self.tab_changed)

        l = self.cg_background_box.layout()
        self.cg_bg_widget = w = Background(self)
        l.addWidget(w, 0, 0, 3, 1)
        self.cover_grid_color_button = b = QPushButton(_('Change &color'),
                                                       self)
        b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        l.addWidget(b, 0, 1)
        b.clicked.connect(self.change_cover_grid_color)
        self.cover_grid_texture_button = b = QPushButton(
            _('Change &background image'), self)
        b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        l.addWidget(b, 1, 1)
        b.clicked.connect(self.change_cover_grid_texture)
        self.cover_grid_default_appearance_button = b = QPushButton(
            _('Restore &default appearance'), self)
        b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        l.addWidget(b, 2, 1)
        b.clicked.connect(self.restore_cover_grid_appearance)
        self.cover_grid_empty_cache.clicked.connect(self.empty_cache)
        self.cover_grid_open_cache.clicked.connect(self.open_cg_cache)
        self.cover_grid_smaller_cover.clicked.connect(
            partial(self.resize_cover, True))
        self.cover_grid_larger_cover.clicked.connect(
            partial(self.resize_cover, False))
        self.cover_grid_reset_size.clicked.connect(self.cg_reset_size)
        self.opt_cover_grid_disk_cache_size.setMinimum(
            self.gui.grid_view.thumbnail_cache.min_disk_cache)
        self.opt_cover_grid_disk_cache_size.setMaximum(
            self.gui.grid_view.thumbnail_cache.min_disk_cache * 100)
        self.opt_cover_grid_width.valueChanged.connect(
            self.update_aspect_ratio)
        self.opt_cover_grid_height.valueChanged.connect(
            self.update_aspect_ratio)
示例#53
0
 def initialize_publisher(self):
     all_publishers = self.db.all_publishers()
     all_publishers.sort(key=lambda x: sort_key(x[1]))
     self.publisher.set_separator(None)
     self.publisher.update_items_cache([x[1] for x in all_publishers])
示例#54
0
numeric_collation = prefs['numeric_collation']


def sort_key_for_name_and_first_letter(x):
    v1 = icu_upper(x.sort or x.name)
    v2 = v1 or ' '
    # The idea is that '9999999999' is larger than any digit so all digits
    # will sort in front. Non-digits will sort according to their ICU first letter
    c = v2[0]
    return (c if numeric_collation and c.isdigit() else '9999999999',
            collation_order(v2), sort_key(v1))


category_sort_keys = {True: {}, False: {}}
category_sort_keys[True]['popularity'] = category_sort_keys[False]['popularity'] = \
    lambda x:(-getattr(x, 'count', 0), sort_key(x.sort or x.name))
category_sort_keys[True]['rating'] = category_sort_keys[False]['rating'] = \
    lambda x:(-getattr(x, 'avg_rating', 0.0), sort_key(x.sort or x.name))
category_sort_keys[True]['name'] = \
    sort_key_for_name_and_first_letter
category_sort_keys[False]['name'] = \
    lambda x:sort_key(x.sort or x.name)


def get_categories(dbcache,
                   sort='name',
                   book_ids=None,
                   first_letter_sort=False):
    if sort not in CATEGORY_SORTS:
        raise ValueError('sort ' + sort + ' not a valid value')
示例#55
0
 def sort_by_name(self):
     bm = self.get_bookmarks()
     bm.sort(key=lambda x:sort_key(x['title']))
     self.set_bookmarks(bm)
     self.edited.emit(bm)
示例#56
0
文件: ajax.py 项目: tokot/calibre
    def ajax_category(self,
                      name,
                      sort='title',
                      num=100,
                      offset=0,
                      sort_order='asc'):
        '''
        Return a dictionary describing the category specified by name. The
        dictionary looks like::

            {
                'category_name': Category display name,
                'base_url': Base URL for this category,
                'total_num': Total numberof items in this category,
                'offset': The offset for the items returned in this result,
                'num': The number of items returned in this result,
                'sort': How the returned items are sorted,
                'sort_order': asc or desc
                'subcategories': List of sub categories of this category.
                'items': List of items in this category,
            }

        Each subcategory is a dictionary of the same form as those returned by
        ajax_categories().

        Each  item is a dictionary of the form::

            {
                'name': Display name,
                'average_rating': Average rating for books in this item,
                'count': Number of books in this item,
                'url': URL to get list of books in this item,
                'has_children': If True this item contains sub categories, look
                for an entry corresponding to this item in subcategories int he
                main dictionary,
            }

        :param sort: How to sort the returned items. CHoices are: name, rating,
                     popularity
        :param sort_order: asc or desc

        To learn how to create subcategories see
        http://manual.calibre-ebook.com/sub_groups.html
        '''
        try:
            num = int(num)
        except:
            raise cherrypy.HTTPError(404, "Invalid num: %r" % num)
        try:
            offset = int(offset)
        except:
            raise cherrypy.HTTPError(404, "Invalid offset: %r" % offset)

        base_url = absurl(self.opts.url_prefix, '/ajax/category/' + name)

        if sort not in ('rating', 'name', 'popularity'):
            sort = 'name'

        if sort_order not in ('asc', 'desc'):
            sort_order = 'asc'

        try:
            dname = decode_name(name)
        except:
            raise cherrypy.HTTPError(
                404, 'Invalid encoding of category name'
                ' %r' % name)

        if dname in ('newest', 'allbooks'):
            if dname == 'newest':
                sort, sort_order = 'timestamp', 'desc'
            raise cherrypy.InternalRedirect(
                '/ajax/books_in/%s/%s?sort=%s&sort_order=%s' %
                (encode_name(dname), encode_name('0'), sort, sort_order))

        fm = self.db.field_metadata
        categories = self.categories_cache()
        hierarchical_categories = self.db.prefs['categories_using_hierarchy']

        subcategory = dname
        toplevel = subcategory.partition('.')[0]
        if toplevel == subcategory:
            subcategory = None
        if toplevel not in categories or toplevel not in fm:
            raise cherrypy.HTTPError(404, 'Category %r not found' % toplevel)

        # Find items and sub categories
        subcategories = []
        meta = fm[toplevel]
        item_names = {}
        children = set()

        if meta['kind'] == 'user':
            fullname = ((toplevel + '.' +
                         subcategory) if subcategory is not None else toplevel)
            try:
                # User categories cannot be applied to books, so this is the
                # complete set of items, no need to consider sub categories
                items = categories[fullname]
            except:
                raise cherrypy.HTTPError(
                    404, 'User category %r not found' % fullname)

            parts = fullname.split('.')
            for candidate in categories:
                cparts = candidate.split('.')
                if len(cparts) == len(parts) + 1 and cparts[:-1] == parts:
                    subcategories.append({
                        'name': cparts[-1],
                        'url': candidate,
                        'icon': category_icon(toplevel, meta)
                    })

            category_name = toplevel[1:].split('.')
            # When browsing by user categories we ignore hierarchical normal
            # columns, so children can be empty

        elif toplevel in hierarchical_categories:
            items = []

            category_names = [
                x.original_name.split('.') for x in categories[toplevel]
                if '.' in x.original_name
            ]

            if subcategory is None:
                children = set(x[0] for x in category_names)
                category_name = [meta['name']]
                items = [
                    x for x in categories[toplevel]
                    if '.' not in x.original_name
                ]
            else:
                subcategory_parts = subcategory.split('.')[1:]
                category_name = [meta['name']] + subcategory_parts

                lsp = len(subcategory_parts)
                children = set(
                    '.'.join(x) for x in category_names
                    if len(x) == lsp + 1 and x[:lsp] == subcategory_parts)
                items = [
                    x for x in categories[toplevel]
                    if x.original_name in children
                ]
                item_names = {
                    x: x.original_name.rpartition('.')[-1]
                    for x in items
                }
                # Only mark the subcategories that have children themselves as
                # subcategories
                children = set(
                    '.'.join(x[:lsp + 1]) for x in category_names
                    if len(x) > lsp + 1 and x[:lsp] == subcategory_parts)
            subcategories = [{
                'name': x.rpartition('.')[-1],
                'url': toplevel + '.' + x,
                'icon': category_icon(toplevel, meta)
            } for x in children]
        else:
            items = categories[toplevel]
            category_name = meta['name']

        for x in subcategories:
            x['url'] = category_url(self.opts.url_prefix, x['url'])
            x['icon'] = icon_url(self.opts.url_prefix, x['icon'])
            x['is_category'] = True

        sort_keygen = {
            'name': lambda x: sort_key(x.sort if x.sort else x.original_name),
            'popularity': lambda x: x.count,
            'rating': lambda x: x.avg_rating
        }
        items.sort(key=sort_keygen[sort], reverse=sort_order == 'desc')
        total_num = len(items)
        items = items[offset:offset + num]
        items = [{
            'name':
            item_names.get(x, x.original_name),
            'average_rating':
            x.avg_rating,
            'count':
            x.count,
            'url':
            books_in_url(self.opts.url_prefix,
                         x.category if x.category else toplevel,
                         x.original_name if x.id is None else unicode(x.id)),
            'has_children':
            x.original_name in children,
        } for x in items]

        return {
            'category_name': category_name,
            'base_url': base_url,
            'total_num': total_num,
            'offset': offset,
            'num': len(items),
            'sort': sort,
            'sort_order': sort_order,
            'subcategories': subcategories,
            'items': items,
        }
示例#57
0
    def __init__(self, fm, pref_name, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        if pref_name == 'column_color_rules':
            self.rule_kind = 'color'
            rule_text = _('column coloring')
        elif pref_name == 'column_icon_rules':
            self.rule_kind = 'icon'
            rule_text = _('column icon')
        elif pref_name == 'cover_grid_icon_rules':
            self.rule_kind = 'emblem'
            rule_text = _('Cover grid emblem')

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(_('Create/edit a {0} rule').format(rule_text))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(
            _('Create a {0} rule by'
              ' filling in the boxes below').format(rule_text))
        l.addWidget(l1, 0, 0, 1, 8)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 8)

        self.l2 = l2 = QLabel(
            _('Add the emblem:') if self.rule_kind ==
            'emblem' else _('Set the'))
        l.addWidget(l2, 2, 0)

        if self.rule_kind == 'color':
            l.addWidget(QLabel(_('color')))
        elif self.rule_kind == 'icon':
            self.kind_box = QComboBox(self)
            for tt, t in icon_rule_kinds:
                self.kind_box.addItem(tt, t)
            l.addWidget(self.kind_box, 2, 1)
            self.kind_box.setToolTip(
                textwrap.fill(
                    _('If you choose composed icons and multiple rules match, then all the'
                      ' matching icons will be combined, otherwise the icon from the'
                      ' first rule to match will be used.')))
        else:
            pass

        self.l3 = l3 = QLabel(_('of the column:'))
        l.addWidget(l3, 2, 2)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 3)

        self.l4 = l4 = QLabel(_('to'))
        l.addWidget(l4, 2, 4)
        if self.rule_kind == 'emblem':
            l3.setVisible(False), self.column_box.setVisible(
                False), l4.setVisible(False)

        def create_filename_box():
            self.filename_box = f = QComboBox()
            self.filenamebox_view = v = QListView()
            v.setIconSize(QSize(32, 32))
            self.filename_box.setView(v)
            self.orig_filenamebox_view = f.view()
            f.setMinimumContentsLength(20), f.setSizeAdjustPolicy(
                f.AdjustToMinimumContentsLengthWithIcon)
            self.populate_icon_filenames()

        if self.rule_kind == 'color':
            self.color_box = ColorButton(parent=self)
            self.color_label = QLabel('Sample text Sample text')
            self.color_label.setTextFormat(Qt.RichText)
            l.addWidget(self.color_box, 2, 5)
            l.addWidget(self.color_label, 2, 6)
            l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
        elif self.rule_kind == 'emblem':
            create_filename_box()
            self.update_filename_box()
            self.filename_button = QPushButton(QIcon(I('document_open.png')),
                                               _('&Add new image'))
            l.addWidget(self.filename_box)
            l.addWidget(self.filename_button, 2, 6)
            l.addWidget(QLabel(_('(Images should be square-ish)')), 2, 7)
            l.setColumnStretch(7, 10)
        else:
            create_filename_box()

            vb = QVBoxLayout()
            self.multiple_icon_cb = QCheckBox(_('Choose &more than one icon'))
            vb.addWidget(self.multiple_icon_cb)
            self.update_filename_box()
            self.multiple_icon_cb.clicked.connect(self.multiple_box_clicked)
            vb.addWidget(self.filename_box)
            l.addLayout(vb, 2, 5)

            self.filename_button = QPushButton(QIcon(I('document_open.png')),
                                               _('&Add icon'))
            l.addWidget(self.filename_button, 2, 6)
            l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7)
            l.setColumnStretch(7, 10)

        self.l5 = l5 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l5, 3, 0, 1, 7)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 8)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                                          _('Add &another condition'))
        l.addWidget(b, 5, 0, 1, 8)
        b.clicked.connect(self.add_blank_condition)

        self.l6 = l6 = QLabel(
            _('You can disable a condition by'
              ' blanking all of its boxes'))
        l.addWidget(l6, 6, 0, 1, 8)

        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok
                                        | QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 8)
        if self.rule_kind != 'color':
            self.remove_button = b = bb.addButton(_('&Remove icon'),
                                                  bb.ActionRole)
            b.setIcon(QIcon(I('minus.png')))
            b.setMenu(QMenu())
            b.setToolTip('<p>' + _(
                'Remove a previously added icon. Note that doing so will cause rules that use it to stop working.'
            ))
            self.update_remove_button()

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        if self.rule_kind == 'color':
            for b in (self.column_box, ):
                b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
                b.setMinimumContentsLength(15)

        for key in sorted(displayable_columns(fm),
                          key=lambda k: sort_key(fm[k]['name'])
                          if k != color_row_key else b''):
            if key == color_row_key and self.rule_kind != 'color':
                continue
            name = all_columns_string if key == color_row_key else fm[key][
                'name']
            if name:
                self.column_box.addItem(name, key)
        self.column_box.setCurrentIndex(0)

        if self.rule_kind == 'color':
            self.color_box.color = '#000'
            self.update_color_label()
            self.color_box.color_changed.connect(self.update_color_label)
        else:
            self.rule_icon_files = []
            self.filename_button.clicked.connect(self.filename_button_clicked)

        self.resize(self.sizeHint())
示例#58
0
文件: ajax.py 项目: tokot/calibre
    def ajax_categories(self):
        '''
        Return the list of top-level categories as a list of dictionaries. Each
        dictionary is of the form::
            {
            'name': Display Name,
            'url':URL that gives the JSON object corresponding to all entries in this category,
            'icon': URL to icon of this category,
            'is_category': False for the All Books and Newest categories, True for everything else
            }

        '''
        ans = {}
        categories = self.categories_cache()
        category_meta = self.db.field_metadata

        def getter(x):
            return category_meta[x]['name']

        displayed_custom_fields = custom_fields_to_display(self.db)

        for category in sorted(categories, key=lambda x: sort_key(getter(x))):
            if len(categories[category]) == 0:
                continue
            if category in ('formats', 'identifiers'):
                continue
            meta = category_meta.get(category, None)
            if meta is None:
                continue
            if category_meta.is_ignorable_field(category) and \
                        category not in displayed_custom_fields:
                continue
            display_name = meta['name']
            if category.startswith('@'):
                category = category.partition('.')[0]
                display_name = category[1:]
            url = force_unicode(category)
            icon = category_icon(category, meta)
            ans[url] = (display_name, icon)

        ans = [{
            'url': k,
            'name': v[0],
            'icon': v[1],
            'is_category': True
        } for k, v in ans.iteritems()]
        ans.sort(key=lambda x: sort_key(x['name']))
        for name, url, icon in [
            (_('All books'), 'allbooks', 'book.png'),
            (_('Newest'), 'newest', 'forward.png'),
        ]:
            ans.insert(0, {
                'name': name,
                'url': url,
                'icon': icon,
                'is_category': False
            })

        for c in ans:
            c['url'] = category_url(self.opts.url_prefix, c['url'])
            c['icon'] = icon_url(self.opts.url_prefix, c['icon'])

        return ans
示例#59
0
    def __init__(self, window, db, on_category=None):
        QDialog.__init__(self, window)
        Ui_TagCategories.__init__(self)
        self.setupUi(self)

        # Remove help icon on title bar
        icon = self.windowIcon()
        self.setWindowFlags(self.windowFlags()
                            & (~Qt.WindowContextHelpButtonHint))
        self.setWindowIcon(icon)

        self.db = db
        self.applied_items = []

        cc_icon = QIcon(I('column.png'))

        self.category_labels = self.category_labels_orig[:]
        category_icons = [
            None,
            QIcon(I('user_profile.png')),
            QIcon(I('series.png')),
            QIcon(I('publisher.png')),
            QIcon(I('tags.png'))
        ]
        category_values = [
            None,
            lambda: [n.replace('|', ',') for (id, n) in self.db.all_authors()],
            lambda: [n for (id, n) in self.db.all_series()],
            lambda: [n for (id, n) in self.db.all_publishers()],
            lambda: self.db.all_tags()
        ]
        category_names = [
            '',
            _('Authors'),
            ngettext('Series', 'Series', 2),
            _('Publishers'),
            _('Tags')
        ]

        cvals = {}
        for key, cc in self.db.custom_field_metadata().iteritems():
            if cc['datatype'] in ['text', 'series', 'enumeration']:
                self.category_labels.append(key)
                category_icons.append(cc_icon)
                category_values.append(
                    lambda col=cc['label']: self.db.all_custom(label=col))
                category_names.append(cc['name'])
            elif cc['datatype'] == 'composite' and \
                    cc['display'].get('make_category', False):
                self.category_labels.append(key)
                category_icons.append(cc_icon)
                category_names.append(cc['name'])
                dex = cc['rec_index']
                cvals = set()
                for book in db.data.iterall():
                    if book[dex]:
                        cvals.add(book[dex])
                category_values.append(lambda s=list(cvals): s)
        self.all_items = []
        self.all_items_dict = {}
        for idx, label in enumerate(self.category_labels):
            if idx == 0:
                continue
            for n in category_values[idx]():
                t = Item(name=n,
                         label=label,
                         index=len(self.all_items),
                         icon=category_icons[idx],
                         exists=True)
                self.all_items.append(t)
                self.all_items_dict[icu_lower(label + ':' + n)] = t

        self.categories = dict.copy(db.prefs.get('user_categories', {}))
        if self.categories is None:
            self.categories = {}
        for cat in self.categories:
            for item, l in enumerate(self.categories[cat]):
                key = icu_lower(':'.join([l[1], l[0]]))
                t = self.all_items_dict.get(key, None)
                if l[1] in self.category_labels:
                    if t is None:
                        t = Item(
                            name=l[0],
                            label=l[1],
                            index=len(self.all_items),
                            icon=category_icons[self.category_labels.index(
                                l[1])],
                            exists=False)
                        self.all_items.append(t)
                        self.all_items_dict[key] = t
                    l[2] = t.index
                else:
                    # remove any references to a category that no longer exists
                    del self.categories[cat][item]

        self.all_items_sorted = sorted(self.all_items,
                                       key=lambda x: sort_key(x.name))
        self.display_filtered_categories(0)

        for v in category_names:
            self.category_filter_box.addItem(v)
        self.current_cat_name = None

        self.apply_button.clicked.connect(self.apply_button_clicked)
        self.unapply_button.clicked.connect(self.unapply_button_clicked)
        self.add_category_button.clicked.connect(self.add_category)
        self.rename_category_button.clicked.connect(self.rename_category)
        self.category_box.currentIndexChanged[int].connect(
            self.select_category)
        self.category_filter_box.currentIndexChanged[int].connect(
            self.display_filtered_categories)
        self.delete_category_button.clicked.connect(self.del_category)
        if islinux:
            self.available_items_box.itemDoubleClicked.connect(self.apply_tags)
        else:
            self.available_items_box.itemActivated.connect(self.apply_tags)
        self.applied_items_box.itemActivated.connect(self.unapply_tags)

        self.populate_category_list()
        if on_category is not None:
            l = self.category_box.findText(on_category)
            if l >= 0:
                self.category_box.setCurrentIndex(l)
        if self.current_cat_name is None:
            self.category_box.setCurrentIndex(0)
            self.select_category(0)
示例#60
0
    def __init__(self, parent, text, mi=None, fm=None, color_field=None,
                 icon_field_key=None, icon_rule_kind=None, doing_emblem=False,
                 text_is_placeholder=False, dialog_is_st_editor=False):
        QDialog.__init__(self, parent)
        Ui_TemplateDialog.__init__(self)
        self.setupUi(self)

        self.coloring = color_field is not None
        self.iconing = icon_field_key is not None
        self.embleming = doing_emblem
        self.dialog_is_st_editor = dialog_is_st_editor

        cols = []
        if fm is not None:
            for key in sorted(displayable_columns(fm),
                              key=lambda k: sort_key(fm[k]['name'] if k != color_row_key else 0)):
                if key == color_row_key and not self.coloring:
                    continue
                from calibre.gui2.preferences.coloring import all_columns_string
                name = all_columns_string if key == color_row_key else fm[key]['name']
                if name:
                    cols.append((name, key))

        self.color_layout.setVisible(False)
        self.icon_layout.setVisible(False)

        if self.coloring:
            self.color_layout.setVisible(True)
            for n1, k1 in cols:
                self.colored_field.addItem(n1, k1)
            self.colored_field.setCurrentIndex(self.colored_field.findData(color_field))
        elif self.iconing or self.embleming:
            self.icon_layout.setVisible(True)
            if self.embleming:
                self.icon_kind_label.setVisible(False)
                self.icon_kind.setVisible(False)
                self.icon_chooser_label.setVisible(False)
                self.icon_field.setVisible(False)

            for n1, k1 in cols:
                self.icon_field.addItem(n1, k1)
            self.icon_file_names = []
            d = os.path.join(config_dir, 'cc_icons')
            if os.path.exists(d):
                for icon_file in os.listdir(d):
                    icon_file = icu_lower(icon_file)
                    if os.path.exists(os.path.join(d, icon_file)):
                        if icon_file.endswith('.png'):
                            self.icon_file_names.append(icon_file)
            self.icon_file_names.sort(key=sort_key)
            self.update_filename_box()

            if self.iconing:
                dex = 0
                from calibre.gui2.preferences.coloring import icon_rule_kinds
                for i,tup in enumerate(icon_rule_kinds):
                    txt,val = tup
                    self.icon_kind.addItem(txt, userData=(val))
                    if val == icon_rule_kind:
                        dex = i
                self.icon_kind.setCurrentIndex(dex)
                self.icon_field.setCurrentIndex(self.icon_field.findData(icon_field_key))

        if dialog_is_st_editor:
            self.buttonBox.setVisible(False)
        else:
            self.new_doc_label.setVisible(False)
            self.new_doc.setVisible(False)
            self.template_name_label.setVisible(False)
            self.template_name.setVisible(False)

        if mi:
            self.mi = mi
        else:
            self.mi = Metadata(_('Title'), [_('Author')])
            self.mi.author_sort = _('Author Sort')
            self.mi.series = ngettext('Series', 'Series', 1)
            self.mi.series_index = 3
            self.mi.rating = 4.0
            self.mi.tags = [_('Tag 1'), _('Tag 2')]
            self.mi.languages = ['eng']
            self.mi.id = 1
            if fm is not None:
                self.mi.set_all_user_metadata(fm.custom_field_metadata())
            else:
                # No field metadata. Grab a copy from the current library so
                # that we can validate any custom column names. The values for
                # the columns will all be empty, which in some very unusual
                # cases might cause formatter errors. We can live with that.
                from calibre.gui2.ui import get_gui
                self.mi.set_all_user_metadata(
                      get_gui().current_db.new_api.field_metadata.custom_field_metadata())
            for col in self.mi.get_all_user_metadata(False):
                self.mi.set(col, (col,), 0)

        # Remove help icon on title bar
        icon = self.windowIcon()
        self.setWindowFlags(self.windowFlags()&(~Qt.WindowType.WindowContextHelpButtonHint))
        self.setWindowIcon(icon)

        self.last_text = ''
        self.highlighter = TemplateHighlighter(self.textbox.document())
        self.textbox.cursorPositionChanged.connect(self.text_cursor_changed)
        self.textbox.textChanged.connect(self.textbox_changed)

        self.textbox.setTabStopWidth(10)
        self.source_code.setTabStopWidth(10)
        self.documentation.setReadOnly(True)
        self.source_code.setReadOnly(True)

        if text is not None:
            if text_is_placeholder:
                self.textbox.setPlaceholderText(text)
                self.textbox.clear()
            else:
                self.textbox.setPlainText(text)
        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText(_('&OK'))
        self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText(_('&Cancel'))
        self.color_copy_button.clicked.connect(self.color_to_clipboard)
        self.filename_button.clicked.connect(self.filename_button_clicked)
        self.icon_copy_button.clicked.connect(self.icon_to_clipboard)

        try:
            with open(P('template-functions.json'), 'rb') as f:
                self.builtin_source_dict = json.load(f, encoding='utf-8')
        except:
            self.builtin_source_dict = {}

        self.funcs = formatter_functions().get_functions()
        self.builtins = formatter_functions().get_builtins()

        func_names = sorted(self.funcs)
        self.function.clear()
        self.function.addItem('')
        self.function.addItems(func_names)
        self.function.setCurrentIndex(0)
        self.function.currentIndexChanged[native_string_type].connect(self.function_changed)
        self.textbox_changed()
        self.rule = (None, '')

        tt = _('Template language tutorial')
        self.template_tutorial.setText(
            '<a href="%s">%s</a>' % (
                localize_user_manual_link('https://manual.calibre-ebook.com/template_lang.html'), tt))
        tt = _('Template function reference')
        self.template_func_reference.setText(
            '<a href="%s">%s</a>' % (
                localize_user_manual_link('https://manual.calibre-ebook.com/generated/en/template_ref.html'), tt))

        self.font_size_box.setValue(gprefs['gpm_template_editor_font_size'])
        self.font_size_box.valueChanged.connect(self.font_size_changed)