def cdb_add_book(ctx, rd, job_id, add_duplicates, filename, library_id): ''' Add a file as a new book. The file contents must be in the body of the request. The response will also have the title/authors/languages read from the metadata of the file/filename. It will contain a `book_id` field specifying the id of the newly added book, or if add_duplicates is not specified and a duplicate was found, no book_id will be present. It will also return the value of `job_id` as the `id` field and `filename` as the `filename` field. ''' db = get_db(ctx, rd, library_id) if ctx.restriction_for(rd, db): raise HTTPForbidden('Cannot use the add book interface with a user who has per library restrictions') if not filename: raise HTTPBadRequest('An empty filename is not allowed') sfilename = sanitize_file_name_unicode(filename) fmt = os.path.splitext(sfilename)[1] fmt = fmt[1:] if fmt else None if not fmt: raise HTTPBadRequest('An filename with no extension is not allowed') if isinstance(rd.request_body_file, BytesIO): raise HTTPBadRequest('A request body containing the file data must be specified') add_duplicates = add_duplicates in ('y', '1') path = os.path.join(rd.tdir, sfilename) rd.request_body_file.name = path rd.request_body_file.seek(0) mi = get_metadata(rd.request_body_file, stream_type=fmt, use_libprs_metadata=True) rd.request_body_file.seek(0) ids, duplicates = db.add_books([(mi, {fmt: rd.request_body_file})], add_duplicates=add_duplicates) ans = {'title': mi.title, 'authors': mi.authors, 'languages': mi.languages, 'filename': filename, 'id': job_id} if ids: ans['book_id'] = ids[0] books_added(ids) return ans
def filename_button_clicked(self): try: path = choose_files(self, 'choose_category_icon', _('Select Icon'), filters=[ ('Images', ['png', 'gif', 'jpg', 'jpeg'])], all_files=False, select_only_single_file=True) if path: icon_path = path[0] icon_name = sanitize_file_name_unicode( os.path.splitext( os.path.basename(icon_path))[0]+'.png') if icon_name not in self.icon_file_names: self.icon_file_names.append(icon_name) self.update_filename_box() try: p = QIcon(icon_path).pixmap(QSize(128, 128)) d = os.path.join(config_dir, 'cc_icons') if not os.path.exists(os.path.join(d, icon_name)): if not os.path.exists(d): os.makedirs(d) with open(os.path.join(d, icon_name), 'wb') as f: f.write(pixmap_to_data(p, format='PNG')) except: traceback.print_exc() self.icon_files.setCurrentIndex(self.icon_files.findText(icon_name)) self.icon_files.adjustSize() except: traceback.print_exc() return
def catalog_generated(self, job): if job.result: # Problems during catalog generation # jobs.results is a list - the first entry is the intended title for the dialog # Subsequent strings are error messages dialog_title = job.result.pop(0) if re.match('warning:', job.result[0].lower()): msg = _("Catalog generation complete, with warnings.") warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True) else: job.result.append("Catalog generation terminated.") error_dialog(self.gui, dialog_title,'\n'.join(job.result),show=True) return if job.failed: return self.gui.job_exception(job) id = self.gui.library_view.model().add_catalog(job.catalog_file_path, job.catalog_title) self.gui.library_view.model().reset() if job.catalog_sync: sync = dynamic.get('catalogs_to_be_synced', set([])) sync.add(id) dynamic.set('catalogs_to_be_synced', sync) self.gui.status_bar.show_message(_('Catalog generated.'), 3000) self.gui.sync_catalogs() if job.fmt not in ['EPUB','MOBI']: export_dir = choose_dir(self.gui, _('Export Catalog Directory'), _('Select destination for %(title)s.%(fmt)s') % dict( title=job.catalog_title, fmt=job.fmt.lower())) if export_dir: destination = os.path.join(export_dir, '%s.%s' % ( sanitize_file_name_unicode(job.catalog_title), job.fmt.lower())) shutil.copyfile(job.catalog_file_path, destination)
def rename_requested(self, name, location): loc = location.replace('/', os.sep) base = os.path.dirname(loc) newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + name, '<p>'+_('Choose a new name for the library <b>%s</b>. ')%name + '<p>'+_('Note that the actual library folder will be renamed.'), text=name) newname = sanitize_file_name_unicode(unicode(newname)) if not ok or not newname or newname == name: return newloc = os.path.join(base, newname) if os.path.exists(newloc): return error_dialog(self.gui, _('Already exists'), _('The folder %s already exists. Delete it first.') % newloc, show=True) if (iswindows and len(newloc) > LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT): return error_dialog(self.gui, _('Too long'), _('Path to library too long. Must be less than' ' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT, show=True) try: os.rename(loc, newloc) except: import traceback error_dialog(self.gui, _('Rename failed'), _('Failed to rename the library at %s. ' 'The most common cause for this is if one of the files' ' in the library is open in another program.') % loc, det_msg=traceback.format_exc(), show=True) return self.stats.rename(location, newloc) self.build_menus() self.gui.iactions['Copy To Library'].build_menus()
def request_bulk_rename(self): names = {unicode(item.data(0, NAME_ROLE).toString()) for item in self.selectedItems()} bad = names & current_container().names_that_must_not_be_changed if bad: return error_dialog(self, _('Cannot rename'), _('The file(s) %s cannot be renamed.') % ('<b>%s</b>' % ', '.join(bad)), show=True) names = sorted(names, key=self.index_of_name) d = QDialog(self) d.l = l = QFormLayout(d) d.setLayout(l) d.prefix = p = QLineEdit(d) p.setText(_('Chapter-')) p.selectAll() d.la = la = QLabel(_( 'All selected files will be renamed to the form prefix-number')) l.addRow(la) l.addRow(_('&Prefix:'), p) d.num = num = QSpinBox(d) num.setMinimum(0), num.setValue(1), num.setMaximum(1000) l.addRow(_('Starting &number:'), num) d.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(d.accept), bb.rejected.connect(d.reject) l.addRow(bb) if d.exec_() == d.Accepted: prefix = sanitize_file_name_unicode(unicode(d.prefix.text())) num = d.num.value() largest = num + len(names) - 1 fmt = '%0{0}d'.format(len(str(largest))) def change_name(name, num): parts = name.split('/') base, ext = parts[-1].rpartition('.')[0::2] parts[-1] = prefix + (fmt % num) + '.' + ext return '/'.join(parts) name_map = {n:change_name(n, num + i) for i, n in enumerate(names)} self.bulk_rename_requested.emit(name_map)
def book_filename(rd, book_id, mi, fmt, as_encoded_unicode=False): au = authors_to_string(mi.authors or [_('Unknown')]) title = mi.title or _('Unknown') ext = (fmt or '').lower() if ext == 'kepub' and 'Kobo Touch' in rd.inheaders.get('User-Agent', ''): ext = 'kepub.epub' fname = '%s - %s_%s.%s' % (title[:30], au[:30], book_id, ext) if as_encoded_unicode: # See https://tools.ietf.org/html/rfc6266 fname = sanitize_file_name_unicode(fname).encode('utf-8') fname = quote(fname).decode('ascii') else: fname = ascii_filename(fname).replace('"', '_') return fname
def name_is_ok(name, show_error): if not name or not name.strip(): return show_error('') and False ext = name.rpartition('.')[-1] if not ext or ext == name: return show_error(_('The file name must have an extension')) and False norm = name.replace('\\', '/') parts = name.split('/') for x in parts: if sanitize_file_name_unicode(x) != x: return show_error(_('The file name contains invalid characters')) and False if current_container().has_name(norm): return show_error(_('This file name already exists in the book')) and False show_error('') return True
def name_is_ok(self): name = unicode(self.name.text()) if not name or not name.strip(): return self.show_error("") ext = name.rpartition(".")[-1] if not ext or ext == name: return self.show_error(_("The file name must have an extension")) norm = name.replace("\\", "/") parts = name.split("/") for x in parts: if sanitize_file_name_unicode(x) != x: return self.show_error(_("The file name contains invalid characters")) if current_container().has_name(norm): return self.show_error(_("This file name already exists in the book")) self.show_error("") return True
def rename_requested(self, name, location): LibraryDatabase = db_class() loc = location.replace('/', os.sep) base = os.path.dirname(loc) old_name = name.replace('&&', '&') newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + old_name, '<p>'+_('Choose a new name for the library <b>%s</b>. ')%name + '<p>'+_('Note that the actual library folder will be renamed.'), text=old_name) newname = sanitize_file_name_unicode(unicode(newname)) if not ok or not newname or newname == old_name: return newloc = os.path.join(base, newname) if os.path.exists(newloc): return error_dialog(self.gui, _('Already exists'), _('The folder %s already exists. Delete it first.') % newloc, show=True) if (iswindows and len(newloc) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT): return error_dialog(self.gui, _('Too long'), _('Path to library too long. Must be less than' ' %d characters.')%LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT, show=True) if not os.path.exists(loc): error_dialog(self.gui, _('Not found'), _('Cannot rename as no library was found at %s. ' 'Try switching to this library first, then switch back ' 'and retry the renaming.')%loc, show=True) return self.gui.library_broker.remove_library(loc) try: os.rename(loc, newloc) except: import traceback det_msg = 'Location: %r New Location: %r\n%s'%(loc, newloc, traceback.format_exc()) error_dialog(self.gui, _('Rename failed'), _('Failed to rename the library at %s. ' 'The most common cause for this is if one of the files' ' in the library is open in another program.') % loc, det_msg=det_msg, show=True) return self.stats.rename(location, newloc) self.build_menus() self.gui.iactions['Copy To Library'].build_menus()
def name_is_ok(self): name = unicode(self.name.text()) if not name or not name.strip(): return self.show_error('') ext = name.rpartition('.')[-1] if not ext or ext == name: return self.show_error(_('The file name must have an extension')) norm = name.replace('\\', '/') parts = name.split('/') for x in parts: if sanitize_file_name_unicode(x) != x: return self.show_error( _('The file name contains invalid characters')) if current_container().has_name(norm): return self.show_error( _('This file name already exists in the book')) self.show_error('') return True
def rename_requested(self, name, location): LibraryDatabase = db_class() loc = location.replace('/', os.sep) base = os.path.dirname(loc) old_name = name.replace('&&', '&') newname, ok = QInputDialog.getText(self.gui, _('Rename') + ' ' + old_name, '<p>'+_('Choose a new name for the library <b>%s</b>. ')%name + '<p>'+_('Note that the actual library folder will be renamed.'), text=old_name) newname = sanitize_file_name_unicode(unicode(newname)) if not ok or not newname or newname == old_name: return newloc = os.path.join(base, newname) if os.path.exists(newloc): return error_dialog(self.gui, _('Already exists'), _('The folder %s already exists. Delete it first.') % newloc, show=True) if (iswindows and len(newloc) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT): return error_dialog(self.gui, _('Too long'), _('Path to library too long. Must be less than' ' %d characters.')%LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT, show=True) if not os.path.exists(loc): error_dialog(self.gui, _('Not found'), _('Cannot rename as no library was found at %s. ' 'Try switching to this library first, then switch back ' 'and retry the renaming.')%loc, show=True) return try: os.rename(loc, newloc) except: import traceback det_msg = 'Location: %r New Location: %r\n%s'%(loc, newloc, traceback.format_exc()) error_dialog(self.gui, _('Rename failed'), _('Failed to rename the library at %s. ' 'The most common cause for this is if one of the files' ' in the library is open in another program.') % loc, det_msg=det_msg, show=True) return self.stats.rename(location, newloc) self.build_menus() self.gui.iactions['Copy To Library'].build_menus()
def catalog_generated(self, job): if job.result: # Problems during catalog generation # jobs.results is a list - the first entry is the intended title for the dialog # Subsequent strings are error messages dialog_title = job.result.pop(0) if re.search('warning', job.result[0].lower()): msg = _("Catalog generation complete, with warnings.") warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True) else: job.result.append("Catalog generation terminated.") error_dialog(self.gui, dialog_title,'\n'.join(job.result),show=True) return if job.failed: return self.gui.job_exception(job) if dynamic.get('catalog_add_to_library', True): id = self.gui.library_view.model().add_catalog(job.catalog_file_path, job.catalog_title) self.gui.library_view.model().beginResetModel(), self.gui.library_view.model().endResetModel() if job.catalog_sync: sync = dynamic.get('catalogs_to_be_synced', set([])) sync.add(id) dynamic.set('catalogs_to_be_synced', sync) self.gui.status_bar.show_message(_('Catalog generated.'), 3000) self.gui.sync_catalogs() if not dynamic.get('catalog_add_to_library', True) or job.fmt not in {'EPUB','MOBI', 'AZW3'}: export_dir = choose_dir(self.gui, _('Export Catalog Directory'), _('Select destination for %(title)s.%(fmt)s') % dict( title=job.catalog_title, fmt=job.fmt.lower())) if export_dir: destination = os.path.join(export_dir, '%s.%s' % ( sanitize_file_name_unicode(job.catalog_title), job.fmt.lower())) try: shutil.copyfile(job.catalog_file_path, destination) except EnvironmentError as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback error_dialog(self, _('Permission denied'), _('Could not open %s. Is it being used by another' ' program?')%destination, det_msg=traceback.format_exc(), show=True) return raise
def catalog_generated(self, job): if job.result: # Problems during catalog generation # jobs.results is a list - the first entry is the intended title for the dialog # Subsequent strings are error messages dialog_title = job.result.pop(0) if re.search('warning', job.result[0].lower()): msg = _("Catalog generation complete, with warnings.") warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True) else: job.result.append("Catalog generation terminated.") error_dialog(self.gui, dialog_title,'\n'.join(job.result),show=True) return if job.failed: return self.gui.job_exception(job) if dynamic.get('catalog_add_to_library', True): id = self.gui.library_view.model().add_catalog(job.catalog_file_path, job.catalog_title) self.gui.library_view.model().beginResetModel(), self.gui.library_view.model().endResetModel() if job.catalog_sync: sync = dynamic.get('catalogs_to_be_synced', set([])) sync.add(id) dynamic.set('catalogs_to_be_synced', sync) self.gui.status_bar.show_message(_('Catalog generated.'), 3000) self.gui.sync_catalogs() if not dynamic.get('catalog_add_to_library', True) or job.fmt not in {'EPUB','MOBI', 'AZW3'}: export_dir = choose_dir(self.gui, _('Export Catalog Directory'), _('Select destination for %(title)s.%(fmt)s') % dict( title=job.catalog_title, fmt=job.fmt.lower())) if export_dir: destination = os.path.join(export_dir, '%s.%s' % ( sanitize_file_name_unicode(job.catalog_title), job.fmt.lower())) try: shutil.copyfile(job.catalog_file_path, destination) except EnvironmentError as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback error_dialog(self.gui, _('Permission denied'), _('Could not open %s. Is it being used by another' ' program?')%destination, det_msg=traceback.format_exc(), show=True) return raise
def catalog_generated(self, job): if job.result: # Problems during catalog generation # jobs.results is a list - the first entry is the intended title for the dialog # Subsequent strings are error messages dialog_title = job.result.pop(0) if re.match('warning:', job.result[0].lower()): msg = _("Catalog generation complete, with warnings.") warning_dialog(self.gui, dialog_title, msg, det_msg='\n'.join(job.result), show=True) else: job.result.append("Catalog generation terminated.") error_dialog(self.gui, dialog_title, '\n'.join(job.result), show=True) return if job.failed: return self.gui.job_exception(job) id = self.gui.library_view.model().add_catalog(job.catalog_file_path, job.catalog_title) self.gui.library_view.model().reset() if job.catalog_sync: sync = dynamic.get('catalogs_to_be_synced', set([])) sync.add(id) dynamic.set('catalogs_to_be_synced', sync) self.gui.status_bar.show_message(_('Catalog generated.'), 3000) self.gui.sync_catalogs() if job.fmt not in ['EPUB', 'MOBI']: export_dir = choose_dir( self.gui, _('Export Catalog Directory'), _('Select destination for %(title)s.%(fmt)s') % dict(title=job.catalog_title, fmt=job.fmt.lower())) if export_dir: destination = os.path.join( export_dir, '%s.%s' % (sanitize_file_name_unicode( job.catalog_title), job.fmt.lower())) shutil.copyfile(job.catalog_file_path, destination)
def filename_button_clicked(self): try: path = choose_files(self, 'choose_category_icon', _('Select Icon'), filters=[('Images', ['png', 'gif', 'jpg', 'jpeg'])], all_files=False, select_only_single_file=True) if path: icon_path = path[0] icon_name = lower( sanitize_file_name_unicode( os.path.splitext(os.path.basename(icon_path))[0] + '.png')) if icon_name not in self.icon_file_names: self.icon_file_names.append(icon_name) self.update_filename_box() self.update_remove_button() try: p = QIcon(icon_path).pixmap(QSize(128, 128)) d = self.icon_folder if not os.path.exists(os.path.join(d, icon_name)): if not os.path.exists(d): os.makedirs(d) with open(os.path.join(d, icon_name), 'wb') as f: f.write(pixmap_to_data(p, format='PNG')) except: import traceback traceback.print_exc() if self.doing_multiple: if icon_name not in self.rule_icon_files: self.rule_icon_files.append(icon_name) self.update_icon_filenames_in_box() else: self.filename_box.setCurrentIndex( self.filename_box.findText(icon_name)) self.filename_box.adjustSize() except: import traceback traceback.print_exc() return
def replace_file(container, name, path, basename, force_mt=None): dirname, base = name.rpartition('/')[0::2] nname = sanitize_file_name_unicode(basename) if dirname: nname = dirname + '/' + nname with open(path, 'rb') as src: if name != nname: count = 0 b, e = nname.rpartition('.')[0::2] while container.exists(nname): count += 1 nname = b + ('_%d.%s' % (count, e)) rename_files(container, {name:nname}) mt = force_mt or container.guess_type(nname) for itemid, q in container.manifest_id_map.iteritems(): if q == nname: for item in container.opf_xpath('//opf:manifest/opf:item[@href and @id="%s"]' % itemid): item.set('media-type', mt) container.dirty(container.opf_name) with container.open(nname, 'wb') as dest: shutil.copyfileobj(src, dest)
def context_menu_handler(self, action=None, category=None, key=None, index=None, search_state=None): 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_unicode(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(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 if action == "edit_item": self.edit(index) return if action == "delete_item": self.tag_item_delete.emit(key, index.id, index.original_name) return if action == "open_editor": self.tags_list_edit.emit(category, key) 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 == "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_author_sort": self.author_sort_edit.emit(self, index, True, False) return if action == "edit_author_link": self.author_sort_edit.emit(self, index, False, True) 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() self.db.prefs.set("tag_browser_hidden_categories", list(self.hidden_categories)) if reset_filter_categories: self._model.set_categories_filter(None) self._model.rebuild_node_tree() except: return
def rename_requested(self, name, location): LibraryDatabase = db_class() loc = location.replace("/", os.sep) base = os.path.dirname(loc) newname, ok = QInputDialog.getText( self.gui, _("Rename") + " " + name, "<p>" + _("Choose a new name for the library <b>%s</b>. ") % name + "<p>" + _("Note that the actual library folder will be renamed."), text=name, ) newname = sanitize_file_name_unicode(unicode(newname)) if not ok or not newname or newname == name: return newloc = os.path.join(base, newname) if os.path.exists(newloc): return error_dialog( self.gui, _("Already exists"), _("The folder %s already exists. Delete it first.") % newloc, show=True ) if iswindows and len(newloc) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT: return error_dialog( self.gui, _("Too long"), _("Path to library too long. Must be less than" " %d characters.") % LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT, show=True, ) if not os.path.exists(loc): error_dialog( self.gui, _("Not found"), _( "Cannot rename as no library was found at %s. " "Try switching to this library first, then switch back " "and retry the renaming." ) % loc, show=True, ) return try: os.rename(loc, newloc) except: import traceback det_msg = "Location: %r New Location: %r\n%s" % (loc, newloc, traceback.format_exc()) error_dialog( self.gui, _("Rename failed"), _( "Failed to rename the library at %s. " "The most common cause for this is if one of the files" " in the library is open in another program." ) % loc, det_msg=det_msg, show=True, ) return self.stats.rename(location, newloc) self.build_menus() self.gui.iactions["Copy To Library"].build_menus()
def sanitize_icon_file_name(self, icon_path): n = lower(sanitize_file_name_unicode( os.path.splitext( os.path.basename(icon_path))[0]+'.png')) return n.replace("'", '_')
def context_menu_handler(self, action=None, category=None, key=None, index=None, search_state=None, use_vl=None): 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_unicode(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(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 if action == 'edit_item_no_vl': item = self.model().get_node(index) item.use_vl = False self.edit(index) return if action == 'edit_item_in_vl': item = self.model().get_node(index) item.use_vl = True self.edit(index) return if action == 'delete_item_in_vl': self.tag_item_delete.emit(key, index.id, index.original_name, self.model().get_book_ids_to_use()) return if action == 'delete_item_no_vl': self.tag_item_delete.emit(key, index.id, index.original_name, None) return if action == 'open_editor': self.tags_list_edit.emit(category, key) 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_author_sort': self.author_sort_edit.emit(self, index, True, False) return if action == 'edit_author_link': self.author_sort_edit.emit(self, index, False, True) 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() 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: return
def context_menu_handler(self, action=None, category=None, key=None, index=None, search_state=None): 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_unicode(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(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 if action == 'edit_item': self.edit(index) return if action == 'delete_item': self.tag_item_delete.emit(key, index.id, index.original_name) return if action == 'open_editor': self.tags_list_edit.emit(category, key) 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 == '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': saved_searches().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_author_sort': self.author_sort_edit.emit(self, index, True, False) return if action == 'edit_author_link': self.author_sort_edit.emit(self, index, False, True) 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() self.db.prefs.set('tag_browser_hidden_categories', list(self.hidden_categories)) if reset_filter_categories: self._model.set_categories_filter(None) self._model.rebuild_node_tree() except: return