def update_ip_info(self): from calibre.gui2.ui import get_gui gui = get_gui() if gui is not None: t = get_gui().iactions['Connect Share'].share_conn_menu.ip_text t = t.strip().strip('[]') self.ip_info.setText(_('Content server listening at: %s') % t)
def download_author(self, id, limit_ids=None): ''' Downloads all of an authors metadata to a path ''' uri = '/maker/%s/calibre' % id print('Downloading metadata for author: ' + uri ) # first get the zipfile tdir = self.download_and_extract(uri) get_gui().current_db.recursive_import(tdir)
def __init__(self, parent=None): QWidget.__init__(self, parent) self.l = l = QVBoxLayout(self) self.la = la = QLabel( _( 'calibre contains an internet server that allows you to' ' access your book collection using a browser from anywhere' ' in the world. Any changes to the settings will only take' ' effect after a server restart.' ) ) la.setWordWrap(True) l.addWidget(la) l.addSpacing(10) self.fl = fl = QFormLayout() l.addLayout(fl) self.opt_port = sb = QSpinBox(self) if options['port'].longdoc: sb.setToolTip(options['port'].longdoc) sb.setRange(1, 65535) sb.valueChanged.connect(self.changed_signal.emit) fl.addRow(options['port'].shortdoc + ':', sb) l.addSpacing(25) self.opt_auth = cb = QCheckBox( _('Require &username and password to access the content server') ) l.addWidget(cb) self.auth_desc = la = QLabel(self) la.setStyleSheet('QLabel { font-size: small; font-style: italic }') la.setWordWrap(True) l.addWidget(la) l.addSpacing(25) self.opt_autolaunch_server = al = QCheckBox( _('Run server &automatically when calibre starts') ) l.addWidget(al) l.addSpacing(25) self.h = h = QHBoxLayout() l.addLayout(h) for text, name in [(_('&Start server'), 'start_server'), (_('St&op server'), 'stop_server'), (_('&Test server'), 'test_server'), (_('Show server &logs'), 'show_logs')]: b = QPushButton(text) b.clicked.connect(getattr(self, name).emit) setattr(self, name + '_button', b) if name == 'show_logs': h.addStretch(10) h.addWidget(b) self.ip_info = QLabel(self) self.update_ip_info() from calibre.gui2.ui import get_gui get_gui().iactions['Connect Share'].share_conn_menu.server_state_changed_signal.connect(self.update_ip_info) l.addSpacing(10) l.addWidget(self.ip_info) l.addStretch(10)
def configure(self): d = Configure(get_gui().current_db, self) if d.exec_() == d.Accepted: if self.current_row is not None: mi = self.view.model().get_book_display_info(self.current_row) if mi is not None: self.refresh(self.current_row, mi=mi)
def get_saved_search_text(self): db = self.current_db try: current_search = self.search.currentText() if not current_search.startswith('search:'): raise ValueError() # This strange expression accounts for the four ways a search can be written: # search:fff, search:"fff", search:"=fff". and search:="fff" current_search = current_search[7:].lstrip('=').strip('"').lstrip('=') current_search = db.saved_search_lookup(current_search) if not current_search: raise ValueError() self.search.set_search_string(current_search) except: from calibre.gui2.ui import get_gui get_gui().status_bar.show_message(_('Current search is not a saved search'), 3000)
def initialize(self, catalog_name, db): self.name = catalog_name from calibre.library.catalogs import FIELDS db = get_gui().current_db self.all_fields = {x for x in FIELDS if x != "all"} | set(db.custom_field_keys()) sort_order = gprefs.get(self.name + "_db_fields_sort_order", {}) fm = db.field_metadata def name(x): if x == "isbn": return "ISBN" if x == "library_name": return _("Library Name") if x.endswith("_index"): return name(x[: -len("_index")]) + " " + _("Number") return fm[x].get("name") or x def key(x): return (sort_order.get(x, 10000), name(x)) self.db_fields.clear() for x in sorted(self.all_fields, key=key): QListWidgetItem(name(x) + " (%s)" % x, self.db_fields).setData(Qt.UserRole, x) if x.startswith("#") and fm[x]["datatype"] == "series": x += "_index" QListWidgetItem(name(x) + " (%s)" % x, self.db_fields).setData(Qt.UserRole, x) # Restore the activated fields from last use fields = frozenset(gprefs.get(self.name + "_db_fields", self.all_fields)) for x in range(self.db_fields.count()): item = self.db_fields.item(x) item.setCheckState(Qt.Checked if unicode(item.data(Qt.UserRole)) in fields else Qt.Unchecked)
def register_keyboard_shortcuts(gui=None, finalize=False): if gui is None: from calibre.gui2.ui import get_gui gui = get_gui() if gui is None: return for unique_name, action in registered_shortcuts.iteritems(): gui.keyboard.unregister_shortcut(unique_name) gui.removeAction(action) registered_shortcuts.clear() for filetype, applications in oprefs['entries'].iteritems(): for application in applications: text = entry_to_icon_text(application, only_text=True) t = _('cover image') if filetype.upper() == 'COVER_IMAGE' else filetype.upper() name = _('Open {0} files with {1}').format(t, text) ac = QAction(gui) unique_name = application['uuid'] func = partial(gui.open_with_action_triggerred, filetype, application) ac.triggered.connect(func) gui.keyboard.register_shortcut(unique_name, name, action=ac, group=_('Open With')) gui.addAction(ac) registered_shortcuts[unique_name] = ac if finalize: gui.keyboard.finalize()
def _get_prefs(self): libraryid = get_library_uuid(get_gui().current_db) if self.current_prefs == None or self.libraryid != libraryid: #logger.debug("self.current_prefs == None(%s) or self.libraryid != libraryid(%s)"%(self.current_prefs == None,self.libraryid != libraryid)) self.libraryid = libraryid self.current_prefs = get_library_config() return self.current_prefs
def initialize(self, catalog_name, db): self.name = catalog_name from calibre.library.catalogs import FIELDS db = get_gui().current_db self.all_fields = {x for x in FIELDS if x != 'all'} | set(db.custom_field_keys()) sort_order, fields = get_saved_field_data(self.name, self.all_fields) fm = db.field_metadata def name(x): if x == 'isbn': return 'ISBN' if x == 'library_name': return _('Library name') if x.endswith('_index'): return name(x[:-len('_index')]) + ' ' + _('Number') return fm[x].get('name') or x def key(x): return (sort_order.get(x, 10000), name(x)) self.db_fields.clear() for x in sorted(self.all_fields, key=key): QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData(Qt.UserRole, x) if x.startswith('#') and fm[x]['datatype'] == 'series': x += '_index' QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData(Qt.UserRole, x) # Restore the activated fields from last use for x in range(self.db_fields.count()): item = self.db_fields.item(x) item.setCheckState(Qt.Checked if unicode_type(item.data(Qt.UserRole)) in fields else Qt.Unchecked)
def save_search_button_clicked(self): from calibre.gui2.ui import get_gui db = get_gui().current_db name = unicode(self.currentText()) if not name.strip(): name = unicode(self.search_box.text()).replace('"', '') name = name.replace('\\', '') if not name: error_dialog(self, _('Create saved search'), _('Invalid saved search name. ' 'It must contain at least one letter or number'), show=True) return if not self.search_box.text(): error_dialog(self, _('Create saved search'), _('There is no search to save'), show=True) return db.saved_search_delete(name) db.saved_search_add(name, unicode(self.search_box.text())) # now go through an initialization cycle to ensure that the combobox has # the new search in it, that it is selected, and that the search box # references the new search instead of the text in the search. self.clear() self.setCurrentIndex(self.findText(name)) self.saved_search_selected(name) self.changed.emit()
def accept(self): from calibre.gui2.ui import get_gui db = get_gui().current_db self.save_current_search() ss = {name:self.searches[name] for name in self.searches} db.saved_search_set_all(ss) QDialog.accept(self)
def copy_search_button_clicked(self): from calibre.gui2.ui import get_gui db = get_gui().current_db idx = self.currentIndex() if idx < 0: return self.search_box.set_search_string(db.saved_search_lookup(unicode(self.currentText())))
def update_button_state(self): from calibre.gui2.ui import get_gui gui = get_gui() is_running = gui.content_server is not None and gui.content_server.is_running self.start_server_button.setEnabled(not is_running) self.stop_server_button.setEnabled(is_running) self.test_server_button.setEnabled(is_running)
def _get_db(self): if self.passed_db: return self.passed_db else: # In the GUI plugin we want current db so we detect when # it's changed. CLI plugin calls need to pass db in. return get_gui().current_db
def contextMenuEvent(self, ev): p = self.page() mf = p.mainFrame() r = mf.hitTestContent(ev.pos()) url = unicode(r.linkUrl().toString()).strip() menu = p.createStandardContextMenu() ca = self.pageAction(p.Copy) for action in list(menu.actions()): if action is not ca: menu.removeAction(action) if not r.isNull(): if url.startswith('http') or url.startswith('file:'): el = r.linkElement() author = el.toPlainText() if unicode(el.attribute('calibre-data')) == u'authors' else None for a, t in [('copy', _('&Copy Link')), ]: ac = getattr(self, '%s_link_action'%a) ac.current_url = url ac.setText(t) menu.addAction(ac) if author is not None: ac = self.manage_author_action ac.current_fmt = author ac.setText(_('Manage %s') % author) menu.addAction(ac) if url.startswith('format:'): parts = url.split(':') try: book_id, fmt = int(parts[1]), parts[2].upper() except: import traceback traceback.print_exc() else: from calibre.gui2.ui import get_gui from calibre.ebooks.oeb.polish.main import SUPPORTED db = get_gui().current_db.new_api ofmt = fmt.upper() if fmt.startswith('ORIGINAL_') else 'ORIGINAL_' + fmt nfmt = ofmt[len('ORIGINAL_'):] fmts = {x.upper() for x in db.formats(book_id)} for a, t in [('remove', _('Delete the %s format')), ('save', _('Save the %s format to disk')), ('restore', _('Restore the %s format')), ('compare', ''), ]: if a == 'restore' and not fmt.startswith('ORIGINAL_'): continue if a == 'compare': if ofmt not in fmts or nfmt not in SUPPORTED: continue t = _('Compare to the %s format') % (fmt[9:] if fmt.startswith('ORIGINAL_') else ofmt) else: t = t % fmt ac = getattr(self, '%s_format_action'%a) ac.current_fmt = (book_id, fmt) ac.setText(t) menu.addAction(ac) if len(menu.actions()) > 0: menu.exec_(ev.globalPos())
def end(self): if gprefs["manual_add_auto_convert"] and self.auto_convert_books: from calibre.gui2.ui import get_gui gui = get_gui() gui.iactions["Convert Books"].auto_convert_auto_add(self.auto_convert_books) self.input_queue.put((None, None, None))
def accept(self): from calibre.gui2.ui import get_gui db = get_gui().current_db if self.current_search_name: self.searches[self.current_search_name] = unicode(self.search_text.toPlainText()) ss = {name:self.searches[name] for name in self.searches} db.saved_search_set_all(ss) QDialog.accept(self)
def generate_cover(self, *args): book_id = self.data.get('id') if book_id is not None: from calibre.ebooks.covers import generate_cover from calibre.gui2.ui import get_gui mi = get_gui().current_db.new_api.get_metadata(book_id) cdata = generate_cover(mi) self.update_cover(cdata=cdata)
def __init__(self, parent=None, search=None): self.initial_search = search Dialog.__init__( self, _('Add a new Saved search'), 'add-saved-search', parent) from calibre.gui2.ui import get_gui db = get_gui().current_db self.searches = {} for name in db.saved_search_names(): self.searches[name] = db.saved_search_lookup(name) self.search_names = {icu_lower(n) for n in db.saved_search_names()}
def doit(self): from calibre.gui2.ui import get_gui library_broker = get_gui().library_broker newdb = library_broker.get_library(self.loc) try: if self.check_for_duplicates: self.find_identical_books_data = newdb.new_api.data_for_find_identical_books() self._doit(newdb) finally: library_broker.prune_loaded_dbs()
def get_saved_field_data(name, all_fields): db = get_gui().current_db val = db.new_api.pref('catalog-field-data-for-' + name) if val is None: sort_order = gprefs.get(name + '_db_fields_sort_order', {}) fields = frozenset(gprefs.get(name+'_db_fields', all_fields)) else: sort_order = val['sort_order'] fields = frozenset(val['fields']) return sort_order, fields
def initialize_saved_search_names(self): from calibre.gui2.ui import get_gui gui = get_gui() try: names = gui.current_db.saved_search_names() except AttributeError: # Happens during gui initialization names = [] self.addItems(names) self.setCurrentIndex(-1)
def set_html(mi, html, web_view): from calibre.gui2.ui import get_gui gui = get_gui() book_id = getattr(mi, 'id', None) if gui and book_id is not None: path = gui.current_db.abspath(book_id, index_is_id=True) if path: web_view.setHtml(html, QUrl.fromLocalFile(os.path.join(path, 'metadata.html'))) return web_view.setHtml(html)
def run_export_action(self): from calibre.gui2.ui import get_gui library_paths = {i.data(Qt.UserRole):i.data(Qt.UserRole+1) for i in self.lib_list.selectedItems()} dbmap = {} gui = get_gui() if gui is not None: db = gui.current_db dbmap[db.library_path] = db.new_api return RunAction(_('Exporting all calibre data...'), _( 'Failed to export data.'), partial(export, self.export_dir, library_paths=library_paths, dbmap=dbmap), parent=self).exec_() == Dialog.Accepted
def arg_library_status(self): ''' Uses curren library type to sync ''' db = get_gui().current_db.new_api type = db.pref('arg_library_type') id = db.pref('arg_id') if type and id: if type=='author': return self.arg_author_status(id) if type=='collection': return self.arg_collection_status(id) return {}
def save_cover(self): from calibre.gui2.ui import get_gui book_id = self.data.get('id') db = get_gui().current_db.new_api path = choose_save_file( self, 'save-cover-from-book-details', _('Choose cover save location'), filters=[(_('JPEG images'), ['jpg', 'jpeg'])], all_files=False, initial_filename='{}.jpeg'.format(db.field_for('title', book_id, default_value='cover')) ) if path: db.copy_cover_to(book_id, path)
def mouseReleaseEvent(self, ev): if ev.button() == Qt.RightButton: tab_name = {'book':'book_details', 'grid':'cover_grid', 'cover_flow':'cover_browser', 'tags':'tag_browser'}.get(self.icname) if tab_name: from calibre.gui2.ui import get_gui gui = get_gui() if gui is not None: gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Look & Feel', tab_name+'_tab'), close_after_initial=True) ev.accept() return return QToolButton.mouseReleaseEvent(self, ev)
def commit(self, book_id): ''' Commits any local changes to the metadata for this book up to the server ''' ids = self.get_arg_ids() if book_id not in ids: print('There is no a*rg identifier for this book') return arg_id, timestamp, version = ids[book_id] repo_version = self.arg_book_status(arg_id) if version!=repo_version: print('This library\'s version is out of date.') return # recompute the id mi = get_gui().current_db.get_metadata(book_id, index_is_id=True, get_cover=True) mi.identifiers['arg'] = "%s.%s" % (mi.identifiers['arg'].split('.')[0], version+1) get_gui().current_db.set_metadata(book_id, mi) mi = get_gui().current_db.get_metadata(book_id, index_is_id=True, get_cover=True) # get the opf that will be posted opf = metadata_to_opf(mi) # now post the metadata return self._post_metadata(arg_id, version, opf, mi.cover)
def do_proceed(self, result): from calibre.gui2.ui import get_gui func = (self.callback if result == self.Accepted else self.cancel_callback) gui = get_gui() gui.proceed_requested.emit(func, self.payload) # Ensure this notification is garbage collected self.vlb.clicked.disconnect() self.callback = self.cancel_callback = self.payload = None self.setParent(None) _proceed_memory.remove(self)
def get_current_db(): ''' This method will try to return the current database in use by the user as efficiently as possible, i.e. without constructing duplicate LibraryDatabase objects. ''' from calibre.gui2.ui import get_gui gui = get_gui() if gui is not None and gui.current_db is not None: return gui.current_db from calibre.library import db return db()
def save_cover(self): from calibre.gui2.ui import get_gui book_id = self.data.get('id') db = get_gui().current_db.new_api path = choose_save_file(self, 'save-cover-from-book-details', _('Choose cover save location'), filters=[(_('JPEG images'), ['jpg', 'jpeg'])], all_files=False, initial_filename='{}.jpeg'.format( sanitize_file_name( db.field_for('title', book_id, default_value='cover')))) if path: db.copy_cover_to(book_id, path)
def mouseReleaseEvent(self, ev): if ev.button() == Qt.RightButton: from calibre.gui2.ui import get_gui gui = get_gui() if self.icname == 'search': gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Search'), close_after_initial=True) ev.accept() return tab_name = {'book':'book_details', 'grid':'cover_grid', 'cover_flow':'cover_browser', 'tags':'tag_browser', 'quickview':'quickview'}.get(self.icname) if tab_name: if gui is not None: gui.iactions['Preferences'].do_config(initial_plugin=('Interface', 'Look & Feel', tab_name+'_tab'), close_after_initial=True) ev.accept() return return QToolButton.mouseReleaseEvent(self, ev)
def handle_changes(changes, gui=None): if not changes: return if gui is None: from calibre.gui2.ui import get_gui gui = get_gui() if gui is None: return refresh_ids = set() added, removed = set(), set() ss_changed = False for change in changes: if isinstance(change, (FormatsAdded, FormatsRemoved, MetadataChanged)): refresh_ids |= change.book_ids elif isinstance(change, BooksAdded): added |= change.book_ids elif isinstance(change, BooksDeleted): removed |= change.book_ids elif isinstance(change, SavedSearchesChanged): ss_changed = True if added and removed: gui.refresh_all() return refresh_ids -= added | removed orig = gui.tags_view.disable_recounting, gui.disable_cover_browser_refresh gui.tags_view.disable_recounting = gui.disable_cover_browser_refresh = True try: if added: gui.current_db.data.books_added(added) gui.iactions['Add Books'].refresh_gui(len(added), recount=False) if removed: next_id = gui.current_view().next_id m = gui.library_view.model() m.ids_deleted(removed) gui.iactions['Remove Books'].library_ids_deleted2(removed, next_id=next_id) if refresh_ids: gui.iactions['Edit Metadata'].refresh_books_after_metadata_edit( refresh_ids) if ss_changed: gui.saved_searches_changed(recount=False) gui.tags_view.disable_recounting = gui.disable_cover_browser_refresh = False gui.tags_view.recount(), gui.refresh_cover_browser() finally: gui.tags_view.disable_recounting, gui.disable_cover_browser_refresh = orig
def get_field_list(fm, use_defaults=False, pref_name='book_display_fields'): from calibre.gui2.ui import get_gui db = get_gui().current_db if use_defaults: src = db.prefs.defaults else: old_val = gprefs.get(pref_name, None) if old_val is not None and not db.prefs.has_setting(pref_name): src = gprefs else: src = db.prefs fieldlist = list(src[pref_name]) names = frozenset(x[0] for x in fieldlist) available = frozenset(fm.displayable_field_keys()) for field in available - names: fieldlist.append((field, True)) return [(f, d) for f, d in fieldlist if f in available]
def get_library_config(): db = get_gui().current_db library_id = get_library_uuid(db) library_config = None # Check whether this is a configuration needing to be migrated # from json into database. If so: get it, set it, wipe it from json. if library_id in old_prefs: #print("get prefs from old_prefs") library_config = old_prefs[library_id] set_library_config(library_config) del old_prefs[library_id] if library_config is None: #print("get prefs from db") library_config = db.prefs.get_namespaced(PREFS_NAMESPACE, PREFS_KEY_SETTINGS, copy.deepcopy(default_prefs)) return library_config
def add_item_specific_entries(menu, data, book_info): search_internet_added = False dt = data['type'] if dt == 'format': add_format_entries(menu, data, book_info) elif dt == 'author': author = data['name'] menu.addAction( init_manage_action(book_info.manage_action, 'authors', author)) if hasattr(book_info, 'search_internet'): menu.sia = sia = create_search_internet_menu( book_info.search_internet, author) menu.addMenu(sia) search_internet_added = True if hasattr(book_info, 'search_requested'): menu.addAction( _('Search calibre for %s') % author, lambda: book_info.search_requested('authors:"={}"'.format( author.replace('"', r'\"')))) elif dt in ('path', 'devpath'): from calibre.gui2.ui import get_gui path = data['loc'] ac = book_info.copy_link_action if isinstance(path, int): path = get_gui().library_view.model().db.abspath(path, index_is_id=True) ac.current_url = path ac.setText(_('Copy path')) menu.addAction(ac) else: field = data.get('field') if field is not None: book_id = int(data['book_id']) value = data['value'] if field == 'identifiers': menu.addAction(book_info.edit_identifiers_action) elif field in ('tags', 'series', 'publisher') or is_category(field): menu.addAction( init_manage_action(book_info.manage_action, field, value)) ac = book_info.remove_item_action ac.data = (field, value, book_id) ac.setText(_('Remove %s from this book') % value) menu.addAction(ac) return search_internet_added
def run_export_action(self): from calibre.gui2.ui import get_gui library_paths = { i.data(Qt.UserRole): i.data(Qt.UserRole + 1) for i in self.lib_list.selectedItems() } dbmap = {} gui = get_gui() if gui is not None: db = gui.current_db dbmap[db.library_path] = db.new_api return RunAction(_('Exporting all calibre data...'), _('Failed to export data.'), partial(export, self.export_dir, library_paths=library_paths, dbmap=dbmap), parent=self).exec_() == Dialog.Accepted
def __init__(self, parent=None, search=None, commit_changes=True, label=None, validate=None): self.initial_search = search self.validate = validate self.label = label self.commit_changes = commit_changes Dialog.__init__(self, _('Add a new Saved search'), 'add-saved-search', parent) from calibre.gui2.ui import get_gui db = get_gui().current_db self.searches = {} for name in db.saved_search_names(): self.searches[name] = db.saved_search_lookup(name) self.search_names = {icu_lower(n): n for n in db.saved_search_names()}
def get_custom_columns_date(self): try: from calibre.gui2.ui import get_gui db = get_gui().current_db except: return None column_types = ['date', 'datetime'] custom_fields = set(db.custom_field_keys()) available_columns = {} for field in list(custom_fields): column = db.field_metadata[field] typ = column['datatype'] if typ in column_types: available_columns[field] = column return available_columns
def details_context_menu_event(view, ev, book_info, add_popup_action=False): url = view.anchorAt(ev.pos()) menu = QMenu(view) menu.addAction(QIcon(I('edit-copy.png')), _('Copy all book details'), partial(copy_all, view)) cm = QMenu(_('Copy link to book'), menu) cm.setIcon(QIcon(I('edit-copy.png'))) copy_links_added = False search_internet_added = False if url and url.startswith('action:'): data = json_loads(from_hex_bytes(url.split(':', 1)[1])) create_copy_links(cm, data) copy_links_added = True search_internet_added = add_item_specific_entries( menu, data, book_info) elif url and not url.startswith('#'): ac = book_info.copy_link_action ac.current_url = url ac.setText(_('Copy link location')) menu.addAction(ac) if not copy_links_added: create_copy_links(cm) if list(cm.actions()): menu.addMenu(cm) if not search_internet_added and hasattr(book_info, 'search_internet'): menu.addSeparator() menu.si = create_search_internet_menu(book_info.search_internet) menu.addMenu(menu.si) for ac in tuple(menu.actions()): if not ac.isEnabled(): menu.removeAction(ac) menu.addSeparator() if add_popup_action: ac = menu.addAction(_('Open the Book details window')) ac.triggered.connect(book_info.show_book_info) else: from calibre.gui2.ui import get_gui ema = get_gui().iactions['Edit Metadata'].menuless_qaction menu.addAction( _('Open the Edit metadata window') + '\t' + ema.shortcut().toString(QKeySequence.NativeText), ema.trigger) if len(menu.actions()) > 0: menu.exec_(ev.globalPos())
def setup_ui(self): from calibre.gui2.ui import get_gui db = get_gui().current_db self.l = l = QVBoxLayout(self) b = self.bb.addButton(_('&Add search'), QDialogButtonBox.ButtonRole.ActionRole) b.setIcon(QIcon(I('plus.png'))) b.clicked.connect(self.add_search) b = self.bb.addButton(_('&Remove search'), QDialogButtonBox.ButtonRole.ActionRole) b.setIcon(QIcon(I('minus.png'))) b.clicked.connect(self.del_search) b = self.bb.addButton(_('&Edit search'), QDialogButtonBox.ButtonRole.ActionRole) b.setIcon(QIcon(I('modified.png'))) b.clicked.connect(self.edit_search) self.slist = QListWidget(self) self.slist.setStyleSheet('QListView::item { padding: 3px }') self.slist.activated.connect(self.edit_search) self.slist.setAlternatingRowColors(True) self.searches = { name: db.saved_search_lookup(name) for name in db.saved_search_names() } self.populate_search_list() if self.initial_search is not None and self.initial_search in self.searches: self.select_search(self.initial_search) elif self.searches: self.slist.setCurrentRow(0) self.slist.currentItemChanged.connect(self.current_index_changed) l.addWidget(self.slist) self.desc = la = QLabel('\xa0') la.setWordWrap(True) l.addWidget(la) l.addWidget(self.bb) self.current_index_changed(self.slist.currentItem()) self.setMinimumHeight(500) self.setMinimumWidth(600)
def build_search_restriction_list(self): self.search_restriction_list_built = True from calibre.gui2.ui import get_gui m = self.ar_menu m.clear() current_restriction_text = None if self.search_restriction.count() > 1: txt = unicode_type(self.search_restriction.itemText(2)) if txt.startswith('*'): current_restriction_text = txt self.search_restriction.clear() current_restriction = self.library_view.model( ).db.data.get_search_restriction_name() m.setIcon(self.checked if current_restriction else self.empty) def add_action(txt, index): self.search_restriction.addItem(txt) txt = self._trim_restriction_name(txt) if txt == current_restriction: a = m.addAction(self.checked, txt if txt else self.no_restriction) else: a = m.addAction(self.empty, txt if txt else self.no_restriction) a.triggered.connect( partial(self.search_restriction_triggered, action=a, index=index)) add_action('', 0) add_action(_('*current search'), 1) dex = 2 if current_restriction_text: add_action(current_restriction_text, 2) dex += 1 for n in sorted(get_gui().current_db.saved_search_names(), key=sort_key): add_action(n, dex) dex += 1
def delete_current_search(self): from calibre.gui2.ui import get_gui db = get_gui().current_db idx = self.currentIndex() if idx <= 0: error_dialog(self, _('Delete current search'), _('No search is selected'), show=True) return if not confirm('<p>'+_('The selected search will be ' '<b>permanently deleted</b>. Are you sure?') +'</p>', 'saved_search_delete', self): return ss = db.saved_search_lookup(unicode(self.currentText())) if ss is None: return db.saved_search_delete(unicode(self.currentText())) self.clear() self.search_box.clear() self.changed.emit()
def save_search_button_clicked(self): from calibre.gui2.ui import get_gui db = get_gui().current_db name = unicode(self.currentText()) if not name.strip(): name = unicode(self.search_box.text()).replace('"', '') if not (name and self.search_box.text()): error_dialog(self, _('Create saved search'), _('There is no search to save'), show=True) return db.saved_search_delete(name) db.saved_search_add(name, unicode(self.search_box.text())) # now go through an initialization cycle to ensure that the combobox has # the new search in it, that it is selected, and that the search box # references the new search instead of the text in the search. self.clear() self.setCurrentIndex(self.findText(name)) self.saved_search_selected(name) self.changed.emit()
def __init__(self, parent, initial_search=None): from calibre.gui2.ui import get_gui db = get_gui().current_db QDialog.__init__(self, parent) Ui_SavedSearchEditor.__init__(self) self.setupUi(self) self.add_search_button.clicked.connect(self.add_search) self.search_name_box.currentIndexChanged[(int)].connect(self.current_index_changed) self.delete_search_button.clicked.connect(self.del_search) self.rename_button.clicked.connect(self.rename_search) self.current_search_name = None self.searches = {} for name in db.saved_search_names(): self.searches[name] = db.saved_search_lookup(name) self.search_names = set([icu_lower(n) for n in db.saved_search_names()]) self.populate_search_list() if initial_search is not None and initial_search in self.searches: self.select_search(initial_search)
def validate_import(self): from calibre.gui2.ui import get_gui g = get_gui() if g is not None: if g.iactions['Connect Share'].content_server_is_running: error_dialog(self, _('Content server running'), _( 'Cannot import while the Content server is running, shut it down first by clicking the' ' "Connect/share" button on the calibre toolbar'), show=True) return False if self.import_panel.stack.currentIndex() == 0: error_dialog(self, _('No folder selected'), _( 'You must select a folder containing the previously exported data that you wish to import'), show=True) return False else: blanks = [] for w in self.imported_lib_widgets: newloc = w.path if not newloc: blanks.append(w.lpath) continue if iswindows and len(newloc) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT: error_dialog(self, _('Too long'), _('Path to library ({0}) too long. Must be less than' ' {1} characters.').format(newloc, LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT), show=True) return False if not os.path.isdir(newloc): error_dialog(self, _('Not a folder'), _('%s is not a folder')%newloc, show=True) return False if os.listdir(newloc): error_dialog(self, _('Folder not empty'), _('%s is not an empty folder')%newloc, show=True) return False if blanks: if len(blanks) == len(self.imported_lib_widgets): error_dialog(self, _('No libraries selected'), _( 'You must specify the location for at least one library'), show=True) return False if not question_dialog(self, _('Some libraries ignored'), _( 'You have chosen not to import some libraries. Proceed anyway?')): return False return True
def refresh(self, idx): ''' Given a cell in the library view, display the information. This method converts the index into the lookup ken ''' if self.lock_qv.isChecked(): return try: bv_row = idx.row() from calibre.gui2.ui import get_gui view = get_gui().library_view.alternate_views.current_view.__class__.__name__ self.current_column = ( self.view.column_map.index('authors') if view == 'GridView' else idx.column()) key = self.view.column_map[self.current_column] book_id = self.view.model().id(bv_row) if self.current_book_id == book_id and self.current_key == key: return self._refresh(book_id, key) except: self.indicate_no_items()
def init_find_in_grouped_search(menu, field, value, book_info): from calibre.gui2.ui import get_gui db = get_gui().current_db fm = db.field_metadata field_name = fm.get(field, {}).get('name', None) if field_name is None: # I don't think this can ever happen, but ... return gsts = db.prefs.get('grouped_search_terms', {}) gsts_to_show = [] for v in gsts: fk = fm.search_term_to_field_key(v) if field in fk: gsts_to_show.append(v) if gsts_to_show: m = QMenu( (_('Search calibre for %s') + '...') % escape_for_menu(value), menu) m.setIcon(QIcon(I('search.png'))) menu.addMenu(m) m.addAction(QIcon(get_icon_path(field, '')), _('in category %s') % escape_for_menu(field_name), lambda g=field: book_info.search_requested( '{}:"={}"'.format(g, value.replace('"', r'\"')), '')) for gst in gsts_to_show: icon_path = get_icon_path(gst, '@') m.addAction( QIcon(icon_path), _('in grouped search %s') % gst, lambda g=gst: book_info.search_requested( '{}:"={}"'.format(g, value.replace('"', r'\"')), '')) else: menu.addAction( QIcon(I('search.png')), _('Search calibre for {val} in category {name}').format( val=escape_for_menu(value), name=escape_for_menu(field_name)), lambda g=field: book_info.search_requested( '{}:"={}"'.format(g, value.replace('"', r'\"')), ''))
def initialize(self, catalog_name, db): self.name = catalog_name from calibre.library.catalogs import FIELDS db = get_gui().current_db self.all_fields = {x for x in FIELDS if x != 'all'} | set(db.custom_field_keys()) sort_order, fields = get_saved_field_data(self.name, self.all_fields) fm = db.field_metadata def name(x): if x == 'isbn': return 'ISBN' if x == 'library_name': return _('Library name') if x.endswith('_index'): return name(x[:-len('_index')]) + ' ' + _('Number') return fm[x].get('name') or x def key(x): return (sort_order.get(x, 10000), name(x)) self.db_fields.clear() for x in sorted(self.all_fields, key=key): QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData(Qt.ItemDataRole.UserRole, x) if x.startswith('#') and fm[x]['datatype'] == 'series': x += '_index' QListWidgetItem(name(x) + ' (%s)' % x, self.db_fields).setData( Qt.ItemDataRole.UserRole, x) # Restore the activated fields from last use for x in range(self.db_fields.count()): item = self.db_fields.item(x) item.setCheckState(Qt.CheckState.Checked if str( item.data(Qt.ItemDataRole.UserRole)) in fields else Qt.CheckState.Unchecked)
def handle_enter_press(self, ev, special_action=None, has_edit_cell=True): if ev.key() in (Qt.Key_Enter, Qt.Key_Return): mods = ev.modifiers() if mods & Qt.CTRL or mods & Qt.ALT or mods & Qt.SHIFT or mods & Qt.META: return if self.state() != self.EditingState and self.hasFocus() and self.currentIndex().isValid(): from calibre.gui2.ui import get_gui ev.ignore() tweak = tweaks['enter_key_behavior'] gui = get_gui() if tweak == 'edit_cell': if has_edit_cell: self.edit(self.currentIndex(), self.EditKeyPressed, ev) else: gui.iactions['Edit Metadata'].edit_metadata(False) elif tweak == 'edit_metadata': gui.iactions['Edit Metadata'].edit_metadata(False) elif tweak == 'do_nothing': pass else: if special_action is not None: special_action(self.currentIndex()) gui.iactions['View'].view_triggered(self.currentIndex()) return True
def register_keyboard_shortcuts(gui=None, finalize=False): if gui is None: from calibre.gui2.ui import get_gui gui = get_gui() if gui is None: return for unique_name, action in registered_shortcuts.iteritems(): gui.keyboard.unregister_shortcut(unique_name) gui.removeAction(action) registered_shortcuts.clear() for filetype, applications in oprefs['entries'].iteritems(): for application in applications: text = entry_to_icon_text(application, only_text=True) t = _('cover image') if filetype.upper() == 'COVER_IMAGE' else filetype.upper() name = _('Open {0} files with {1}').format(t, text) ac = QAction(gui) unique_name = application['uuid'] ac.triggered.connect(partial(gui.open_with_action_triggerred, filetype, application)) gui.keyboard.register_shortcut(unique_name, name, action=ac, group=_('Open With')) gui.addAction(ac) registered_shortcuts[unique_name] = ac if finalize: gui.keyboard.finalize()
def is_category(field): from calibre.db.categories import find_categories from calibre.gui2.ui import get_gui gui = get_gui() fm = gui.current_db.field_metadata return field in {x[0] for x in find_categories(fm) if fm.is_custom_field(x[0])}
def __init__(self, parent, text, mi=None, fm=None, color_field=None, icon_field_key=None, icon_rule_kind=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 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: self.icon_layout.setVisible(True) 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() 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=QVariant(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 mi: self.mi = mi else: self.mi = Metadata(_('Title'), [_('Author')]) self.mi.author_sort = _('Author Sort') self.mi.series = _('Series') self.mi.series_index = 3 self.mi.rating = 4.0 self.mi.tags = [_('Tag 1'), _('Tag 2')] self.mi.languages = ['eng'] 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()) # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags()&(~Qt.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: self.textbox.setPlainText(text) self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK')) self.buttonBox.button(QDialogButtonBox.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[str].connect(self.function_changed) self.textbox_changed() self.rule = (None, '') tt = _('Template language tutorial') self.template_tutorial.setText( '<a href="http://manual.calibre-ebook.com/template_lang.html">' '%s</a>'%tt) tt = _('Template function reference') self.template_func_reference.setText( '<a href="http://manual.calibre-ebook.com/template_ref.html">' '%s</a>'%tt)
def set_library_config(library_config): get_gui().current_db.prefs.set_namespaced(PREFS_NAMESPACE, PREFS_KEY_SETTINGS, library_config)
def current_db(): from calibre.gui2.ui import get_gui return (getattr(current_db, 'ans', None) or get_gui().current_db).new_api
def edit_tags(self): from calibre.gui2.dialogs.tag_editor import TagEditor d = TagEditor(self, get_gui().current_db, current_tags=list(filter(None, [x.strip() for x in self.query.text().split(',')]))) if d.exec_() == d.Accepted: self.query.setText(', '.join(d.tags))
def can_use_tag_editor(self): return self.SUBJECT is RuleEdit.SUBJECT and 'matches' not in self.match_type.currentData() and get_gui() is not None
def __init__( self, title=_('Choose Files'), filters=[], add_all_files_filter=True, parent=None, modal=True, name='', mode=QFileDialog.ExistingFiles, default_dir=u'~', no_save_dir=False, combine_file_and_saved_dir=False ): from calibre.gui2 import dynamic, sanitize_env_vars from calibre.gui2.ui import get_gui gui = get_gui() adapt_menubar = gui.bars_manager.adapt_menu_bar_for_dialog if gui is not None else Dummy() QObject.__init__(self) ftext = '' if filters: for filter in filters: text, extensions = filter extensions = ['*'+(i if i.startswith('.') else '.'+i) for i in extensions] ftext += '%s (%s);;'%(text, ' '.join(extensions)) if add_all_files_filter or not ftext: ftext += 'All files (*)' if ftext.endswith(';;'): ftext = ftext[:-2] self.dialog_name = dialog_name(name, title) self.selected_files = None self.fd = None if combine_file_and_saved_dir: bn = os.path.basename(default_dir) prev = dynamic.get(self.dialog_name, os.path.expanduser(u'~')) if os.path.exists(prev): if os.path.isfile(prev): prev = os.path.dirname(prev) else: prev = os.path.expanduser(u'~') initial_dir = os.path.join(prev, bn) elif no_save_dir: initial_dir = os.path.expanduser(default_dir) else: initial_dir = dynamic.get(self.dialog_name, os.path.expanduser(default_dir)) if not isinstance(initial_dir, string_or_bytes): initial_dir = os.path.expanduser(default_dir) if not initial_dir or (not os.path.exists(initial_dir) and not ( mode == QFileDialog.AnyFile and (no_save_dir or combine_file_and_saved_dir))): initial_dir = select_initial_dir(initial_dir) self.selected_files = [] use_native_dialog = 'CALIBRE_NO_NATIVE_FILEDIALOGS' not in os.environ with sanitize_env_vars(): opts = QFileDialog.Option() if not use_native_dialog: opts |= QFileDialog.DontUseNativeDialog if mode == QFileDialog.AnyFile: with adapt_menubar: f = QFileDialog.getSaveFileName(parent, title, initial_dir, ftext, "", opts) if f and f[0]: self.selected_files.append(f[0]) elif mode == QFileDialog.ExistingFile: with adapt_menubar: f = QFileDialog.getOpenFileName(parent, title, initial_dir, ftext, "", opts) if f and f[0] and os.path.exists(f[0]): self.selected_files.append(f[0]) elif mode == QFileDialog.ExistingFiles: with adapt_menubar: fs = QFileDialog.getOpenFileNames(parent, title, initial_dir, ftext, "", opts) if fs and fs[0]: for f in fs[0]: f = unicode_type(f) if not f: continue if not os.path.exists(f): # QFileDialog for some reason quotes spaces # on linux if there is more than one space in a row f = unquote(f) if f and os.path.exists(f): self.selected_files.append(f) else: if mode == QFileDialog.Directory: opts |= QFileDialog.ShowDirsOnly with adapt_menubar: f = unicode_type(QFileDialog.getExistingDirectory(parent, title, initial_dir, opts)) if os.path.exists(f): self.selected_files.append(f) if self.selected_files: self.selected_files = [unicode_type(q) for q in self.selected_files] saved_loc = self.selected_files[0] if os.path.isfile(saved_loc): saved_loc = os.path.dirname(saved_loc) if not no_save_dir: dynamic[self.dialog_name] = saved_loc self.accepted = bool(self.selected_files)
def context_menu_handler(self, action=None, category=None, key=None, index=None, search_state=None, use_vl=None, is_first_letter=False): if not action: return try: if action == 'set_icon': try: path = choose_files(self, 'choose_category_icon', _('Change icon for: %s')%key, filters=[ ('Images', ['png', 'gif', 'jpg', 'jpeg'])], all_files=False, select_only_single_file=True) if path: path = path[0] p = QIcon(path).pixmap(QSize(128, 128)) d = os.path.join(config_dir, 'tb_icons') if not os.path.exists(d): os.makedirs(d) with open(os.path.join(d, 'icon_' + sanitize_file_name(key)+'.png'), 'wb') as f: f.write(pixmap_to_data(p, format='PNG')) path = os.path.basename(f.name) self._model.set_custom_category_icon(key, unicode_type(path)) self.recount() except: import traceback traceback.print_exc() return if action == 'clear_icon': self._model.set_custom_category_icon(key, None) self.recount() return def set_completion_data(category): try: completion_data = self.db.new_api.all_field_names(category) except: completion_data = None self.itemDelegate().set_completion_data(completion_data) if action == 'edit_item_no_vl': item = self.model().get_node(index) item.use_vl = False set_completion_data(category) self.edit(index) return if action == 'edit_item_in_vl': item = self.model().get_node(index) item.use_vl = True set_completion_data(category) self.edit(index) return if action == 'delete_item_in_vl': tag = index.tag children = index.child_tags() self.tag_item_delete.emit(key, tag.id, tag.original_name, self.model().get_book_ids_to_use(), children) return if action == 'delete_item_no_vl': tag = index.tag children = index.child_tags() self.tag_item_delete.emit(key, tag.id, tag.original_name, None, children) return if action == 'open_editor': self.tags_list_edit.emit(category, key, is_first_letter) return if action == 'manage_categories': self.edit_user_category.emit(category) return if action == 'search': self._toggle(index, set_to=search_state) return if action == "raw_search": from calibre.gui2.ui import get_gui get_gui().get_saved_search_text(search_name='search:' + key) return if action == 'add_to_category': tag = index.tag if len(index.children) > 0: for c in index.all_children(): self.add_item_to_user_cat.emit(category, c.tag.original_name, c.tag.category) self.add_item_to_user_cat.emit(category, tag.original_name, tag.category) return if action == 'add_subcategory': self.add_subcategory.emit(key) return if action == 'search_category': self._toggle(index, set_to=search_state) return if action == 'delete_user_category': self.delete_user_category.emit(key) return if action == 'delete_search': self.model().db.saved_search_delete(key) self.rebuild_saved_searches.emit() return if action == 'delete_item_from_user_category': tag = index.tag if len(index.children) > 0: for c in index.children: self.del_item_from_user_cat.emit(key, c.tag.original_name, c.tag.category) self.del_item_from_user_cat.emit(key, tag.original_name, tag.category) return if action == 'manage_searches': self.saved_search_edit.emit(category) return if action == 'edit_authors': self.author_sort_edit.emit(self, index, False, False, is_first_letter) return if action == 'edit_author_sort': self.author_sort_edit.emit(self, index, True, False, is_first_letter) return if action == 'edit_author_link': self.author_sort_edit.emit(self, index, False, True, False) return reset_filter_categories = True if action == 'hide': self.hidden_categories.add(category) elif action == 'show': self.hidden_categories.discard(category) elif action == 'categorization': changed = self.collapse_model != category self._model.collapse_model = category if changed: reset_filter_categories = False gprefs['tags_browser_partition_method'] = category elif action == 'defaults': self.hidden_categories.clear() elif action == 'add_tag': item = self.model().get_node(index) if item is not None: self.apply_to_selected_books(item) return elif action == 'remove_tag': item = self.model().get_node(index) if item is not None: self.apply_to_selected_books(item, True) return self.db.new_api.set_pref('tag_browser_hidden_categories', list(self.hidden_categories)) if reset_filter_categories: self._model.set_categories_filter(None) self._model.rebuild_node_tree() except Exception: import traceback traceback.print_exc() return
def add_item_specific_entries(menu, data, book_info, copy_menu, search_menu): from calibre.gui2.ui import get_gui search_internet_added = False find_action = book_info.find_in_tag_browser_action dt = data['type'] def add_copy_action(name): copy_menu.addAction(QIcon(I('edit-copy.png')), _('The text: {}').format(name), lambda: QApplication.instance().clipboard().setText(name)) if dt == 'format': add_format_entries(menu, data, book_info, copy_menu, search_menu) elif dt == 'author': author = data['name'] if data['url'] != 'calibre': ac = book_info.copy_link_action ac.current_url = data['url'] ac.setText(_('&Author link')) copy_menu.addAction(ac) add_copy_action(author) init_find_in_tag_browser(search_menu, find_action, 'authors', author) init_find_in_grouped_search(search_menu, 'authors', author, book_info) menu.addAction(init_manage_action(book_info.manage_action, 'authors', author)) if hasattr(book_info, 'search_internet'): search_menu.addSeparator() search_menu.sim = create_search_internet_menu(book_info.search_internet, author) for ac in search_menu.sim.actions(): search_menu.addAction(ac) ac.setText(_('Search {0} for {1}').format(ac.text(), author)) search_internet_added = True if hasattr(book_info, 'remove_item_action'): ac = book_info.remove_item_action book_id = get_gui().library_view.current_id ac.data = ('authors', author, book_id) ac.setText(_('Remove %s from this book') % escape_for_menu(author)) menu.addAction(ac) elif dt in ('path', 'devpath'): path = data['loc'] ac = book_info.copy_link_action if isinstance(path, int): path = get_gui().library_view.model().db.abspath(path, index_is_id=True) ac.current_url = path ac.setText(_('The location of the book')) copy_menu.addAction(ac) else: field = data.get('field') if field is not None: book_id = int(data['book_id']) value = remove_value = data['value'] if field == 'identifiers': ac = book_info.copy_link_action ac.current_url = value ac.setText(_('&Identifier')) copy_menu.addAction(ac) if data.get('url'): book_info.copy_identifiers_url_action.current_url = data['url'] copy_menu.addAction(book_info.copy_identifiers_url_action) remove_value = data['id_type'] init_find_in_tag_browser(search_menu, find_action, field, remove_value) init_find_in_grouped_search(search_menu, field, remove_value, book_info) menu.addAction(book_info.edit_identifiers_action) elif field in ('tags', 'series', 'publisher') or is_category(field): add_copy_action(value) init_find_in_tag_browser(search_menu, find_action, field, value) init_find_in_grouped_search(search_menu, field, value, book_info) menu.addAction(init_manage_action(book_info.manage_action, field, value)) elif field == 'languages': remove_value = langnames_to_langcodes((value,)).get(value, 'Unknown') init_find_in_tag_browser(search_menu, find_action, field, value) init_find_in_grouped_search(search_menu, field, value, book_info) ac = book_info.remove_item_action ac.data = (field, remove_value, book_id) ac.setText(_('Remove %s from this book') % escape_for_menu(value)) menu.addAction(ac) return search_internet_added