def apply_changes(self): self.changed.add(self.book_id) if self.db is None: # break_cycles has already been called, don't know why this should # happen but a user reported it return True for widget in self.basic_metadata_widgets: try: if hasattr(widget, 'validate_for_commit'): title, msg, det_msg = widget.validate_for_commit() if title is not None: error_dialog(self, title, msg, det_msg=det_msg, show=True) return False widget.commit(self.db, self.book_id) self.books_to_refresh |= getattr(widget, 'books_to_refresh', set()) except (IOError, OSError) as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback fname = getattr(err, 'filename', None) p = 'Locked file: %s\n\n'%fname if fname else '' error_dialog(self, _('Permission denied'), _('Could not change the on disk location of this' ' book. Is it open in another program?'), det_msg=p+traceback.format_exc(), show=True) return False raise for widget in getattr(self, 'custom_metadata_widgets', []): self.books_to_refresh |= widget.commit(self.book_id) self.db.commit() rows = self.db.refresh_ids(list(self.books_to_refresh)) if rows: self.rows_to_refresh |= set(rows) return True
def rename_category(self): self.save_category() cat_name = unicode_type(self.input_box.text()).strip() if cat_name == '': return False if not self.current_cat_name: return False comps = [c.strip() for c in cat_name.split('.') if c.strip()] if len(comps) == 0 or '.'.join(comps) != cat_name: error_dialog(self, _('Invalid name'), _('That name contains leading or trailing periods, ' 'multiple periods in a row or spaces before ' 'or after periods.')).exec_() return False for c in self.categories: if strcmp(c, cat_name) == 0: error_dialog(self, _('Name already used'), _('That name is already used, perhaps with different case.')).exec_() return False # The order below is important because of signals self.categories[cat_name] = self.categories[self.current_cat_name] del self.categories[self.current_cat_name] self.current_cat_name = None self.populate_category_list() self.input_box.clear() self.category_box.setCurrentIndex(self.category_box.findText(cat_name)) return True
def _add_formats(self, paths): added = False if not paths: return added bad_perms = [] for _file in paths: _file = os.path.abspath(_file) if not os.access(_file, os.R_OK): bad_perms.append(_file) continue nfile = run_plugins_on_import(_file) if nfile is not None: _file = nfile stat = os.stat(_file) size = stat.st_size ext = os.path.splitext(_file)[1].lower().replace('.', '') timestamp = utcfromtimestamp(stat.st_mtime) for row in range(self.formats.count()): fmt = self.formats.item(row) if fmt.ext.lower() == ext: self.formats.takeItem(row) break Format(self.formats, ext, size, path=_file, timestamp=timestamp) self.changed = True added = True if bad_perms: error_dialog(self, _('No permission'), _('You do not have ' 'permission to read the following files:'), det_msg='\n'.join(bad_perms), show=True) return added
def accept(self): searches = tprefs['saved_searches'] all_names = {x['name'] for x in searches} - {self.original_name} n = unicode(self.search_name.text()).strip() search = self.search if not n: return error_dialog(self, _('Must specify name'), _( 'You must specify a search name'), show=True) if n in all_names: return error_dialog(self, _('Name exists'), _( 'Another search with the name %s already exists') % n, show=True) search['name'] = n f = unicode(self.find.text()) if not f: return error_dialog(self, _('Must specify find'), _( 'You must specify a find expression'), show=True) search['find'] = f r = unicode(self.replace.text()) search['replace'] = r search['dot_all'] = bool(self.dot_all.isChecked()) search['case_sensitive'] = bool(self.case_sensitive.isChecked()) search['mode'] = self.mode_box.mode if self.search_index == -1: searches.append(search) else: searches[self.search_index] = search tprefs.set('saved_searches', searches) Dialog.accept(self)
def finalize_apply(self): db = self.gui.current_db db.commit() if self.apply_pd is not None: self.apply_pd.hide() if self.apply_failures: msg = [] for i, tb in self.apply_failures: title = db.title(i, index_is_id=True) authors = db.authors(i, index_is_id=True) if authors: authors = [x.replace('|', ',') for x in authors.split(',')] title += ' - ' + authors_to_string(authors) msg.append(title+'\n\n'+tb+'\n'+('*'*80)) error_dialog(self.gui, _('Some failures'), _('Failed to apply updated metadata for some books' ' in your library. Click "Show Details" to see ' 'details.'), det_msg='\n\n'.join(msg), show=True) changed_books = len(self.applied_ids or ()) self.refresh_gui(self.applied_ids) self.apply_id_map = [] self.apply_pd = None try: if callable(self.apply_callback): self.apply_callback(list(self.applied_ids)) finally: self.apply_callback = None if changed_books: QApplication.alert(self.gui, 2000)
def ask_user(self): # Ask the user for a factor by which to multiply all font sizes factor, ok = QInputDialog.getDouble( self.gui, 'Enter a magnification factor', 'Allow font sizes in the book will be multiplied by the specified factor', value=2, min=0.1, max=4 ) if ok: # Ensure any in progress editing the user is doing is present in the container self.boss.commit_all_editors_to_container() try: self.magnify_fonts(factor) except Exception: # Something bad happened report the error to the user import traceback error_dialog(self.gui, _('Failed to magnify fonts'), _( 'Failed to magnify fonts, click "Show details" for more info'), det_msg=traceback.format_exc(), show=True) # Revert to the saved restore point self.boss.revert_requested(self.boss.global_undo.previous_container) else: # Show the user what changes we have made, allowing her to # revert them if necessary self.boss.show_current_diff() # Update the editor UI to take into account all the changes we # have made self.boss.apply_container_update_to_gui()
def add_recipient(self): to = unicode(self.address.text()).strip() if not to: return error_dialog( self, _('Need address'), _('You must specify an address'), show=True) formats = ','.join([x.strip().upper() for x in unicode(self.formats.text()).strip().split(',') if x.strip()]) if not formats: return error_dialog( self, _('Need formats'), _('You must specify at least one format to send'), show=True) opts = email_config().parse() if to in opts.accounts: return error_dialog( self, _('Already exists'), _('The recipient %s already exists') % to, show=True) acc = opts.accounts acc[to] = [formats, False, False] c = email_config() c.set('accounts', acc) alias = unicode(self.alias.text()).strip() if alias: opts.aliases[to] = alias c.set('aliases', opts.aliases) subject = unicode(self.subject.text()).strip() if subject: opts.subjects[to] = subject c.set('subjects', opts.subjects) self.create_item(alias or to, to, checked=True)
def _download_done(self, ret, tb): if not self.isVisible(): return self.reject() if tb is not None: error_dialog(self, _('Download failed'), _( 'Failed to download external resources, click "Show Details" for more information.'), det_msg=tb, show=True) self.reject() else: replacements, failures = ret if failures: tb = ['{}\n\t{}\n'.format(url, err) for url, err in failures.iteritems()] if not replacements: error_dialog(self, _('Download failed'), _( 'Failed to download external resources, click "Show Details" for more information.'), det_msg='\n'.join(tb), show=True) self.reject() return else: warning_dialog(self, _('Some downloads failed'), _( 'Failed to download some external resources, click "Show Details" for more information.'), det_msg='\n'.join(tb), show=True) self.state = 2 self.wait.msg = _('Updating resources in book...') self.wait.start() t = ngettext( 'Successfully processed the external resource', 'Successfully processed {} external resources', len(replacements)).format(len(replacements)) if failures: t += '<br>' + ngettext('Could not download one image', 'Could not download {} images', len(failures)).format(len(failures)) self.success.setText('<p style="text-align:center">' + t) resources = self.choose_resources.resources t = Thread(name='ReplaceResources', target=self.replace_resources, args=(resources, replacements)) t.daemon = True t.start()
def accept(self): txt = unicode(self.textbox.toPlainText()).rstrip() if self.coloring: if self.colored_field.currentIndex() == -1: error_dialog(self, _('No column chosen'), _('You must specify a column to be colored'), show=True) return if not txt: error_dialog(self, _('No template provided'), _('The template box cannot be empty'), show=True) return self.rule = (unicode(self.colored_field.itemData( self.colored_field.currentIndex()) or ''), txt) elif self.iconing: rt = unicode(self.icon_kind.itemData(self.icon_kind.currentIndex()) or '') self.rule = (rt, unicode(self.icon_field.itemData( self.icon_field.currentIndex()) or ''), txt) elif self.embleming: self.rule = ('icon', 'title', txt) else: self.rule = ('', txt) QDialog.accept(self)
def rebuild_it(self): from calibre.ebooks.tweak import get_tools, WorkerError src_dir = self._exploded det_msg = None of = PersistentTemporaryFile('_tweak_rebuild.'+self.current_format.lower()) of.close() of = of.name self._cleanup_files.append(of) try: rebuilder = get_tools(self.current_format)[1] rebuilder(src_dir, of) except WorkerError as e: det_msg = e.orig_tb except: import traceback det_msg = traceback.format_exc() finally: self.hide_msg() if det_msg is not None: error_dialog(self, _('Failed to rebuild file'), _('Failed to rebuild %s. For more information, click ' '"Show details".')%self.current_format, det_msg=det_msg, show=True) return None return of
def process_results(self): while self.continue_processing: try: self.process_result(self.worker.rq.get_nowait()) except Empty: break if self.continue_processing: self.covers_view.clear_failed() if self.worker.error is not None: error_dialog(self, _('Download failed'), _('Failed to download any covers, click' ' "Show details" for details.'), det_msg=self.worker.error, show=True) num = self.covers_view.model().rowCount() if num < 2: txt = _('Could not find any covers for <b>%s</b>')%self.book.title else: txt = _('Found <b>%(num)d</b> covers of %(title)s. ' 'Pick the one you like best.')%dict(num=num-1, title=self.title) self.msg.setText(txt) self.finished.emit()
def add_category(self): self.save_category() cat_name = unicode_type(self.input_box.text()).strip() if cat_name == '': return False comps = [c.strip() for c in cat_name.split('.') if c.strip()] if len(comps) == 0 or '.'.join(comps) != cat_name: error_dialog(self, _('Invalid name'), _('That name contains leading or trailing periods, ' 'multiple periods in a row or spaces before ' 'or after periods.')).exec_() return False for c in sorted(self.categories.keys(), key=sort_key): if strcmp(c, cat_name) == 0 or \ (icu_lower(cat_name).startswith(icu_lower(c) + '.') and not cat_name.startswith(c + '.')): error_dialog(self, _('Name already used'), _('That name is already used, perhaps with different case.')).exec_() return False if cat_name not in self.categories: self.category_box.clear() self.current_cat_name = cat_name self.categories[cat_name] = [] self.applied_items = [] self.populate_category_list() self.input_box.clear() self.category_box.setCurrentIndex(self.category_box.findText(cat_name)) return True
def validate(self): rule = self.rule if not rule['query']: error_dialog(self, _('Query required'), _( 'You must provide a value for the tag to match'), show=True) return False return True
def create_button_clicked(self): self.changed_signal.emit() name = unicode(self.function_name.currentText()) if name in self.funcs: error_dialog(self.gui, _('Template functions'), _('Name %s already used')%(name,), show=True) return if self.argument_count.value() == 0: box = warning_dialog(self.gui, _('Template functions'), _('Argument count should be -1 or greater than zero. ' 'Setting it to zero means that this function cannot ' 'be used in single function mode.'), det_msg='', show=False) box.bb.setStandardButtons(box.bb.standardButtons() | QDialogButtonBox.Cancel) box.det_msg_toggle.setVisible(False) if not box.exec_(): return try: prog = unicode(self.program.toPlainText()) cls = compile_user_function(name, unicode(self.documentation.toPlainText()), self.argument_count.value(), prog) self.funcs[name] = cls self.build_function_names_box(scroll_to=name) except: error_dialog(self.gui, _('Template functions'), _('Exception while compiling function'), show=True, det_msg=traceback.format_exc())
def __init__(self, gui, initial_filter=FILTER_UPDATE_AVAILABLE): SizePersistedDialog.__init__(self, gui, 'Plugin Updater plugin:plugin updater dialog') self.gui = gui self.forum_link = None self.zip_url = None self.model = None self.do_restart = False self._initialize_controls() self._create_context_menu() try: display_plugins = read_available_plugins(raise_error=True) except Exception: display_plugins = [] import traceback error_dialog(self.gui, _('Update Check Failed'), _('Unable to reach the plugin index page.'), det_msg=traceback.format_exc(), show=True) if display_plugins: self.model = DisplayPluginModel(display_plugins) self.proxy_model = DisplayPluginSortFilterModel(self) self.proxy_model.setSourceModel(self.model) self.plugin_view.setModel(self.proxy_model) self.plugin_view.resizeColumnsToContents() self.plugin_view.selectionModel().currentRowChanged.connect(self._plugin_current_changed) self.plugin_view.doubleClicked.connect(self.install_button.click) self.filter_combo.setCurrentIndex(initial_filter) self._select_and_focus_view() else: self.filter_combo.setEnabled(False) # Cause our dialog size to be restored from prefs or created on first usage self.resize_dialog()
def handle_key_press(self, ev): editor = self.parent() if ev.key() == KEY and ev.modifiers() & MODIFIER: at = self.get_active_template(editor.textCursor()) if at is not None: if at.jump_to_next(editor) is None: self.active_templates.remove(at) else: if not at.remains_active(): self.active_templates.remove(at) ev.accept() return True lst, self.last_selected_text = self.last_selected_text, editor.selected_text if self.last_selected_text: editor.textCursor().insertText('') ev.accept() return True c, text = get_text_before_cursor(editor) snip, trigger = find_matching_snip(text, editor.syntax, self.snip_func) if snip is None: error_dialog(self.parent(), _('No snippet found'), _( 'No matching snippet was found'), show=True) self.last_selected_text = self.last_selected_text or lst return True template = expand_template(editor, trigger, snip['template']) if template.has_tab_stops: self.active_templates.append(template) if lst: for ts in template: ts.apply_selected_text(lst) ev.accept() return True return False
def ensure_single_instance(args, open_at): try: from calibre.utils.lock import singleinstance si = singleinstance(singleinstance_name) except Exception: import traceback error_dialog(None, _('Cannot start viewer'), _( 'Failed to start viewer, could not insure only a single instance of the viewer is running. Click "Show Details" for more information'), det_msg=traceback.format_exc(), show=True) raise SystemExit(1) if not si: if len(args) > 1: t = RC(print_error=True, socket_address=viewer_socket_address()) t.start() t.join(3.0) if t.is_alive() or t.conn is None: error_dialog(None, _('Connect to viewer failed'), _( 'Unable to connect to existing viewer window, try restarting the viewer.'), show=True) raise SystemExit(1) t.conn.send((os.path.abspath(args[1]), open_at)) t.conn.close() prints('Opened book in existing viewer instance') raise SystemExit(0) listener = create_listener() return listener
def pre_commit_check(self): definitions = self.get_definitions() # Verify the search/replace in the edit widgets has been # included to the list of search/replace definitions edit_search = self.sr_search.regex if edit_search: edit_replace = unicode(self.sr_replace.text()) found = False for search, replace in definitions: if search == edit_search and replace == edit_replace: found = True break if not found and not question_dialog(self, _('Unused Search & Replace definition'), _('The search / replace definition being edited ' ' has not been added to the list of definitions. ' 'Do you wish to continue with the conversion ' '(the definition will not be used)?')): return False # Verify all search expressions are valid for search, replace in definitions: try: re.compile(search) except Exception as err: error_dialog(self, _('Invalid regular expression'), _('Invalid regular expression: %s')%err, show=True) return False return True
def get_selected_format_metadata(self, db, id_): old = prefs['read_file_metadata'] if not old: prefs['read_file_metadata'] = True try: row = self.formats.currentRow() fmt = self.formats.item(row) if fmt is None: if self.formats.count() == 1: fmt = self.formats.item(0) if fmt is None: error_dialog(self, _('No format selected'), _('No format selected')).exec_() return None, None ext = fmt.ext.lower() if fmt.path is None: stream = db.format(id_, ext, as_file=True, index_is_id=True) else: stream = open(fmt.path, 'r+b') try: with stream: mi = get_metadata(stream, ext) return mi, ext except: error_dialog(self, _('Could not read metadata'), _('Could not read metadata from %s format')%ext).exec_() return None, None finally: if old != prefs['read_file_metadata']: prefs['read_file_metadata'] = old
def add_profile(self, clicked): if self.stacks.currentIndex() == 0: src, title = self.options_to_profile() try: compile_recipe(src) except Exception as err: error_dialog(self, _('Invalid input'), _('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_() return profile = src else: src = unicode(self.source_code.toPlainText()) try: title = compile_recipe(src).title except Exception as err: error_dialog(self, _('Invalid input'), _('<p>Could not create recipe. Error:<br>%s')%str(err)).exec_() return profile = src.replace('BasicUserRecipe', 'AdvancedUserRecipe') 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()
def open_book(self, path=None, edit_file=None, clear_notify_data=True): if not self._check_before_open(): return if not hasattr(path, 'rpartition'): path = choose_files(self.gui, 'open-book-for-tweaking', _('Choose book'), [(_('Books'), [x.lower() for x in SUPPORTED])], all_files=False, select_only_single_file=True) if not path: return path = path[0] ext = path.rpartition('.')[-1].upper() if ext not in SUPPORTED: return error_dialog(self.gui, _('Unsupported format'), _('Tweaking is only supported for books in the %s formats.' ' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)), show=True) if not os.path.exists(path): return error_dialog(self.gui, _('File not found'), _( 'The file %s does not exist.') % path, show=True) for name in tuple(editors): self.close_editor(name) self.gui.preview.clear() self.container_count = -1 if self.tdir: shutil.rmtree(self.tdir, ignore_errors=True) self.tdir = PersistentTemporaryDirectory() self._edit_file_on_open = edit_file self._clear_notify_data = clear_notify_data self.gui.blocking_job('open_book', _('Opening book, please wait...'), self.book_opened, get_container, path, tdir=self.mkdtemp())
def select_cover(self, *args): files = choose_images(self, 'change cover dialog', _('Choose cover for ') + self.dialog.title.current_val) if not files: return _file = files[0] if _file: _file = os.path.abspath(_file) if not os.access(_file, os.R_OK): d = error_dialog(self, _('Cannot read'), _('You do not have permission to read the file: ') + _file) d.exec_() return cf, cover = None, None try: cf = open(_file, "rb") cover = cf.read() except IOError as e: d = error_dialog(self, _('Error reading file'), _("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e)) d.exec_() if cover: orig = self.current_val self.current_val = cover if self.current_val is None: self.current_val = orig error_dialog(self, _("Not a valid picture"), _file + _(" is not a valid picture"), show=True)
def report_save_error(self, tb): if self.doing_terminal_save: prints(tb, file=sys.stderr) return error_dialog(self.gui, _('Could not save'), _('Saving of the book failed. Click "Show Details"' ' for more information.'), det_msg=tb, show=True)
def configure_plugin(self): for index in self.sources_view.selectionModel().selectedRows(): plugin = self.sources_model.data(index, Qt.UserRole) if plugin is not NONE: return self.do_config(plugin) error_dialog(self, _('No source selected'), _('No source selected, cannot configure.'), show=True)
def _view_calibre_books(self, ids): db = self.gui.current_db views = [] for id_ in ids: try: formats = db.formats(id_, index_is_id=True) except: error_dialog(self.gui, _('Cannot view'), _('This book no longer exists in your library'), show=True) self.update_history([], remove=set([id_])) continue title = db.title(id_, index_is_id=True) if not formats: error_dialog(self.gui, _('Cannot view'), _('%s has no available formats.')%(title,), show=True) continue formats = formats.upper().split(',') fmt = formats[0] for format in prefs['input_format_order']: if format in formats: fmt = format break views.append((id_, title)) self.view_format_by_id(id_, fmt) self.update_history(views)
def cover_from_format(self, *args): ext = self.formats_manager.get_selected_format() if ext is None: return if ext == 'pdf': return self.get_pdf_cover() try: mi, ext = self.formats_manager.get_selected_format_metadata(self.db, self.book_id) except (IOError, OSError) as err: if getattr(err, 'errno', None) == errno.EACCES: # Permission denied import traceback fname = err.filename if err.filename else 'file' error_dialog(self, _('Permission denied'), _('Could not open %s. Is it being used by another' ' program?')%fname, det_msg=traceback.format_exc(), show=True) return raise if mi is None: return cdata = None if mi.cover and os.access(mi.cover, os.R_OK): cdata = open(mi.cover).read() elif mi.cover_data[1] is not None: cdata = mi.cover_data[1] if cdata is None: error_dialog(self, _('Could not read cover'), _('Could not read cover from %s format')%ext).exec_() return self.update_cover(cdata, ext)
def rename_block_tag(self, editor, new_name): c = editor.textCursor() block, offset = c.block(), c.positionInBlock() tag = None while True: tag = find_closest_containing_tag(block, offset) if tag is None: break block, offset = tag.start_block, tag.start_offset if tag.name in { 'address', 'article', 'aside', 'blockquote', 'center', 'dir', 'fieldset', 'isindex', 'menu', 'noframes', 'hgroup', 'noscript', 'pre', 'section', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'p', 'div', 'dd', 'dl', 'ul', 'ol', 'li', 'body', 'td', 'th'}: break tag = None if tag is not None: closing_tag = find_closing_tag(tag) if closing_tag is None: return error_dialog(editor, _('Invalid HTML'), _( 'There is an unclosed %s tag. You should run the Fix HTML tool' ' before trying to rename tags.') % tag.name, show=True) rename_tag(c, tag, closing_tag, new_name, insert=tag.name in {'body', 'td', 'th', 'li'}) else: return error_dialog(editor, _('No found'), _( 'No suitable block level tag was found to rename'), show=True)
def request_edit(self, name): item = self.item_from_name(name) if item is not None: self._request_edit(item) else: error_dialog(self, _('Cannot edit'), _('No item with the name: %s was found') % name, show=True)
def find(self, search): self.last_search = search try: self.document.search(search) except StopIteration: error_dialog(self, _('No matches found'), _('<b>No matches</b> for the search phrase <i>%s</i> were found.')%(search,)).exec_() self.search.search_done(True)
def find_portable_library(): base = get_portable_base() if base is None: return import glob candidates = [os.path.basename(os.path.dirname(x)) for x in glob.glob( os.path.join(base, u'*%smetadata.db'%os.sep))] if not candidates: candidates = [u'Calibre Library'] lp = prefs['library_path'] if not lp: lib = os.path.join(base, candidates[0]) else: lib = None q = os.path.basename(lp) for c in candidates: c = c if c.lower() == q.lower(): lib = os.path.join(base, c) break if lib is None: lib = os.path.join(base, candidates[0]) if len(lib) > 74: error_dialog(None, _('Path too long'), _("Path to Calibre Portable (%s) " 'too long. Must be less than 59 characters.')%base, show=True) raise AbortInit() prefs.set('library_path', lib) if not os.path.exists(lib): os.mkdir(lib)
def choose_vl_triggerred(self): from calibre.gui2.tweak_book.widgets import QuickOpen, emphasis_style db = self.library_view.model().db virt_libs = db.prefs.get('virtual_libraries', {}) if not virt_libs: return error_dialog(self, _('No Virtual libraries'), _( 'No Virtual libraries present, create some first'), show=True) example = '<pre>{0}S{1}ome {0}B{1}ook {0}C{1}ollection</pre>'.format( '<span style="%s">' % emphasis_style(), '</span>') chars = '<pre style="%s">sbc</pre>' % emphasis_style() help_text = _('''<p>Quickly choose a Virtual library by typing in just a few characters from the library name into the field above. For example, if want to choose the VL: {example} Simply type in the characters: {chars} and press Enter.''').format(example=example, chars=chars) d = QuickOpen( sorted(virt_libs.keys(), key=sort_key), parent=self, title=_('Choose Virtual library'), name='vl-open', level1=' ', help_text=help_text) if d.exec_() == d.Accepted and d.selected_result: self.apply_virtual_library(library=d.selected_result)
def find(self, forwards=True): text = unicode(self.search_text.text()).strip() flags = QWebPage.FindFlags(0) if forwards else QWebPage.FindBackward d = self.dest_list if d.count() == 1: flags |= QWebPage.FindWrapsAroundDocument if not self.view.findText(text, flags) and text: if d.count() == 1: return error_dialog(self, _('No match found'), _('No match found for: %s')%text, show=True) delta = 1 if forwards else -1 current = unicode(d.currentItem().data(Qt.DisplayRole) or '') next_index = (d.currentRow() + delta)%d.count() next = unicode(d.item(next_index).data(Qt.DisplayRole) or '') msg = '<p>'+_('No matches for %(text)s found in the current file [%(current)s].' ' Do you want to search in the %(which)s file [%(next)s]?') msg = msg%dict(text=text, current=current, next=next, which=_('next') if forwards else _('previous')) if question_dialog(self, _('No match found'), msg): self.pending_search = self.find_next if forwards else self.find_previous d.setCurrentRow(next_index)
def run_program(entry, path, parent): # noqa import re cmdline = entry_to_cmdline(entry, path) flags = subprocess.CREATE_DEFAULT_ERROR_MODE | subprocess.CREATE_NEW_PROCESS_GROUP if re.match(r'"[^"]+?(.bat|.cmd|.com)"', cmdline, flags=re.I): flags |= subprocess.CREATE_NO_WINDOW console = ' (console)' else: flags |= subprocess.DETACHED_PROCESS console = '' print('Running Open With commandline%s:' % console, repr(entry['cmdline']), ' |==> ', repr(cmdline)) try: with sanitize_env_vars(): winutil.run_cmdline(cmdline, flags, 2000) except Exception as err: return error_dialog( parent, _('Failed to run'), _('Failed to run program, click "Show details" for more information' ), det_msg='Command line: %r\n%s' % (cmdline, as_unicode(err)))
def save_settings(self): xpaths = self.xpaths if not xpaths: return error_dialog(self, _('No XPaths'), _('No XPaths have been entered'), show=True) if not self.check(): return name, ok = QInputDialog.getText(self, _('Choose name'), _('Choose a name for these settings')) if ok: name = unicode_type(name).strip() if name: saved = self.prefs.get('xpath_toc_settings', {}) # in JSON all keys have to be strings saved[name] = { unicode_type(i): x for i, x in enumerate(xpaths) } self.prefs.set('xpath_toc_settings', saved) self.setup_load_button()
def set_text_alignment(self, editor, value): ''' Set the text-align property on the current block tag(s) ''' editor.highlighter.join() block_tag_names = BLOCK_TAG_NAMES - { 'body' } # ignore body since setting text-align globally on body is almost never what is wanted tags = [] c = editor.textCursor() if c.hasSelection(): start, end = min(c.anchor(), c.position()), max(c.anchor(), c.position()) c.setPosition(start) block = c.block() while block.isValid() and block.position() < end: ud = block.userData() if ud is not None: for tb in ud.tags: if tb.is_start and not tb.closing and tb.name.lower( ) in block_tag_names: nblock, boundary = next_tag_boundary( block, tb.offset) if boundary is not None and not boundary.is_start and not boundary.self_closing: tags.append(Tag(block, tb, nblock, boundary)) block = block.next() if not tags: c = editor.textCursor() block, offset = c.block(), c.positionInBlock() tag = find_closest_containing_block_tag(block, offset, block_tag_names) if tag is None: return error_dialog( editor, _('Not in a block tag'), _('Cannot change text alignment as the cursor is not inside a block level tag, such as a <p> or <div> tag.' ), show=True) tags = [tag] for tag in reversed(tags): set_style_property(tag, 'text-align', value, editor)
def _view_books(self, rows): if not rows or len(rows) == 0: return error_dialog(self.gui, _('Cannot view'), _('No books selected'), show=True) if not self._view_check(len(rows)): return if self.gui.current_view() is self.gui.library_view: ids = [] m = self.gui.library_view.model().id for r in rows: try: ids.append(m(r)) except Exception: pass if ids: self._view_calibre_books(ids) else: paths = self.gui.current_view().model().paths(rows) for path in paths: self.view_device_book(path)
def update_metadata(self): ''' Set the metadata in the files in the selected book's record to match the current metadata in the database. ''' from calibre.ebooks.metadata.meta import set_metadata from calibre.gui2 import error_dialog, info_dialog # Get currently selected books rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, 'Cannot update metadata', 'No books selected', show=True) # Map the rows to book ids ids = list(map(self.gui.library_view.model().id, rows)) db = self.db.new_api for book_id in ids: # Get the current metadata for this book from the db mi = db.get_metadata(book_id, get_cover=True, cover_as_data=True) fmts = db.formats(book_id) if not fmts: continue for fmt in fmts: fmt = fmt.lower() # Get a python file object for the format. This will be either # an in memory file or a temporary on disk file ffile = db.format(book_id, fmt, as_file=True) ffile.seek(0) # Set metadata in the format set_metadata(ffile, mi, fmt) ffile.seek(0) # Now replace the file in the calibre library with the updated # file. We dont use add_format_with_hooks as the hooks were # already run when the file was first added to calibre. db.add_format(book_id, fmt, ffile, run_hooks=False) info_dialog(self, 'Updated files', 'Updated the metadata in the files of %d book(s)'%len(ids), show=True)
def accept(self): if not self.name_is_ok: return error_dialog(self, _('No name specified'), _( 'You must specify a name for the new file, with an extension, for example, chapter1.html'), show=True) tprefs['auto_link_stylesheets'] = self.link_css.isChecked() name = unicode_type(self.name.text()) name, ext = name.rpartition('.')[0::2] name = (name + '.' + ext.lower()).replace('\\', '/') mt = guess_type(name) if not self.file_data: if mt in OEB_DOCS: self.file_data = template_for('html').encode('utf-8') if tprefs['auto_link_stylesheets']: data = add_stylesheet_links(current_container(), name, self.file_data) if data is not None: self.file_data = data self.using_template = True elif mt in OEB_STYLES: self.file_data = template_for('css').encode('utf-8') self.using_template = True self.file_name = name QDialog.accept(self)
def restore_database(self): m = self.gui.library_view.model() db = m.db if (iswindows and len(db.library_path) > LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT): return error_dialog( self.gui, _('Too long'), _('Path to library too long. Must be less than' ' %d characters. Move your library to a location with' ' a shorter path using Windows Explorer, then point' ' calibre to the new location and try again.') % LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT, show=True) from calibre.gui2.dialogs.restore_library import restore_database m = self.gui.library_view.model() m.stop_metadata_backup() db = m.db db.prefs.disable_setting = True if restore_database(db, self.gui): self.gui.library_moved(db.library_path, call_close=False)
def validate(self): title = self.title.text().strip() if not title: error_dialog(self, _('Title required'), _( 'You must give your news source a title'), show=True) return False if self.feeds.count() < 1: error_dialog(self, _('Feed required'), _( 'You must add at least one feed to your news source'), show=True) return False try: compile_recipe(self.recipe_source) except Exception as err: error_dialog(self, _('Invalid recipe'), _( 'Failed to compile the recipe, with syntax error: %s' % err), show=True) return False return True
def link_stylesheets(self, names): s = self.categories['styles'] sheets = [unicode(s.child(i).data(0, NAME_ROLE) or '') for i in xrange(s.childCount())] if not sheets: return error_dialog(self, _('No stylesheets'), _( 'This book currently has no stylesheets. You must first create a stylesheet' ' before linking it.'), show=True) d = QDialog(self) d.l = l = QVBoxLayout(d) d.setLayout(l) d.setWindowTitle(_('Choose stylesheets')) d.la = la = QLabel(_('Choose the stylesheets to link. Drag and drop to re-arrange')) la.setWordWrap(True) l.addWidget(la) d.s = s = QListWidget(d) l.addWidget(s) s.setDragEnabled(True) s.setDropIndicatorShown(True) s.setDragDropMode(self.InternalMove) s.setAutoScroll(True) s.setDefaultDropAction(Qt.MoveAction) for name in sheets: i = QListWidgetItem(name, s) flags = Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsDragEnabled | Qt.ItemIsSelectable i.setFlags(flags) i.setCheckState(Qt.Checked) d.r = r = QCheckBox(_('Remove existing links to stylesheets')) r.setChecked(tprefs['remove_existing_links_when_linking_sheets']) l.addWidget(r) d.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) bb.accepted.connect(d.accept), bb.rejected.connect(d.reject) l.addWidget(bb) if d.exec_() == d.Accepted: tprefs['remove_existing_links_when_linking_sheets'] = r.isChecked() sheets = [unicode(s.item(il).text()) for il in xrange(s.count()) if s.item(il).checkState() == Qt.Checked] if sheets: self.link_stylesheets_requested.emit(names, sheets, r.isChecked())
def __init__(self, parent, db): QObject.__init__(self, parent) self.internet_connection_failed = False self._parent = parent self.no_internet_msg = _( 'Cannot download news as no internet connection ' 'is active') self.no_internet_dialog = d = error_dialog(self._parent, self.no_internet_msg, _('No internet connection'), show_copy_button=False) d.setModal(False) self.recipe_model = RecipeModel() self.db = db self.lock = QMutex(QMutex.RecursionMode.Recursive) self.download_queue = set() self.news_menu = QMenu() self.news_icon = QIcon(I('news.png')) self.scheduler_action = QAction(QIcon(I('scheduler.png')), _('Schedule news download'), self) self.news_menu.addAction(self.scheduler_action) self.scheduler_action.triggered[bool].connect(self.show_dialog) self.cac = QAction(QIcon(I('user_profile.png')), _('Add or edit a custom news source'), self) self.cac.triggered[bool].connect(self.customize_feeds) self.news_menu.addAction(self.cac) self.news_menu.addSeparator() self.all_action = self.news_menu.addAction( _('Download all scheduled news sources'), self.download_all_scheduled) self.timer = QTimer(self) self.timer.start(int(self.INTERVAL * 60 * 1000)) self.timer.timeout.connect(self.check) self.oldest = gconf['oldest_news'] QTimer.singleShot(5 * 1000, self.oldest_check)
def manage_series(self): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) == 0 or not self.is_library_selected: return error_dialog(self.gui, _('Cannot manage series'), _('No books selected'), show=True) series_columns = self.get_series_columns() books = self.get_selected_books(rows, series_columns) db = self.gui.library_view.model().db all_series = db.all_series() all_series.sort(key=lambda x : sort_key(x[1])) d = SeriesDialog(self.gui, books, all_series, series_columns) d.exec_() if d.result() != d.Accepted: return updated_ids = [] # Prevent the TagView from updating due to signals from the database self.gui.tags_view.blockSignals(True) num_added = 0 try: for book in books: calibre_id = book.id() if calibre_id is None: db.import_book(book.get_mi_to_persist(), []) num_added += 1 elif book.is_series_changed() or book.is_title_changed() or book.is_pubdate_changed(): updated_ids.append(calibre_id) db.set_metadata(calibre_id, book.get_mi_to_persist(), commit=False) db.commit() finally: self.gui.tags_view.blockSignals(False) if num_added > 0: self.gui.library_view.model().books_added(num_added) if hasattr(self.gui, 'db_images'): self.gui.db_images.reset() self.gui.library_view.model().refresh_ids(updated_ids) self.gui.tags_view.recount()
def ebook_edit_format(self, book_id, fmt): ''' Also called from edit_metadata formats list. In that context, SUPPORTED check was already done. ''' db = self.gui.library_view.model().db from calibre.gui2.tweak_book import tprefs tprefs.refresh() # In case they were changed in a Tweak Book process path = db.new_api.format_abspath(book_id, fmt) if path is None: return error_dialog(self.gui, _('File missing'), _( 'The %s format is missing from the calibre library. You should run' ' library maintenance.') % fmt, show=True) tweak = 'ebook-edit' try: self.gui.setCursor(Qt.CursorShape.BusyCursor) if tprefs['update_metadata_from_calibre']: db.new_api.embed_metadata((book_id,), only_fmts={fmt}) notify = '%d:%s:%s:%s' % (book_id, fmt, db.library_id, db.library_path) self.gui.job_manager.launch_gui_app(tweak, kwargs=dict(path=path, notify=notify)) time.sleep(2) finally: self.gui.unsetCursor()
def current_changed(self, item): name = self.current_name = unicode_type( item.data(Qt.DisplayRole) or '') path = self.container.name_to_abspath(name) # Ensure encoding map is populated root = self.container.parsed(name) nasty = root.xpath('//*[local-name()="head"]/*[local-name()="p"]') if nasty: body = root.xpath('//*[local-name()="body"]') if not body: return error_dialog( self, _('Bad markup'), _('This book has severely broken markup, its ToC cannot be edited.' ), show=True) for x in reversed(nasty): body[0].insert(0, x) self.container.commit_item(name, keep_parsed=True) self.view.load_path(path, self.current_frag) self.current_frag = None self.dest_label.setText(self.base_msg + '<br>' + _('File:') + ' ' + name + '<br>' + _('Top of the file'))
def select_import_folder(self): path = choose_dir(self, 'choose-export-folder-for-import', _('Select folder with exported data')) if path is None: return if not question_dialog( self, _('Are you sure?'), _('Importing calibre data means all libraries, settings, plugins, etc will be imported. This is' ' a security risk, only proceed if the data you are importing was previously generated by you, using the calibre' ' export functionality.')): return try: self.importer = Importer(path) except Exception as e: import traceback return error_dialog(self, _('Not valid'), _('The folder {0} is not valid: {1}').format( path, as_unicode(e)), det_msg=traceback.format_exc(), show=True) self.setup_select_libraries_panel() self.import_panel.stack.setCurrentIndex(1)
def request_delete(self): names = { unicode(item.data(0, NAME_ROLE).toString()) for item in self.selectedItems() } bad = names & current_container().names_that_must_not_be_removed if bad: return error_dialog(self, _('Cannot delete'), _('The file(s) %s cannot be deleted.') % ('<b>%s</b>' % ', '.join(bad)), show=True) text = self.categories['text'] children = (text.child(i) for i in xrange(text.childCount())) spine_removals = [(unicode(item.data(0, NAME_ROLE).toString()), item.isSelected()) for item in children] other_removals = { unicode(item.data(0, NAME_ROLE).toString()) for item in self.selectedItems() if unicode(item.data(0, CATEGORY_ROLE).toString()) != 'text' } self.delete_requested.emit(spine_removals, other_removals)
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_type(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 main_menu_triggered(self): from calibre_plugins.EmbedComicMetadata.main import embed_into_comic, import_to_calibre i = prefs["main_import"] # Check the preferences for what should be done if (i and prefs['read_cbi'] and prefs['read_cix']) or (not i and prefs['cbi_embed'] and prefs['cix_embed']): action = "both" elif (i and prefs['read_cbi']) or (not i and prefs['cbi_embed']): action = "cbi" elif (i and prefs['read_cix']) or (not i and prefs['cix_embed']): action = "cix" else: return error_dialog(self.gui, _L['Cannot update metadata'], _L['No embed format selected'], show=True) if i: import_to_calibre(self, action) else: embed_into_comic(self, action)
def _launch_viewer(self, name=None, viewer='ebook-viewer', internal=True): self.gui.setCursor(Qt.BusyCursor) try: if internal: args = [viewer] if isosx and 'ebook' in viewer: args.append('--raise-window') if name is not None: args.append(name) self.gui.job_manager.launch_gui_app(viewer, kwargs=dict(args=args)) else: if iswindows: winutil = plugins['winutil'][0] ext = name.rpartition('.')[-1] if ext: try: prog = winutil.file_association( unicode_type('.' + ext)) except Exception: prog = None if prog and prog.lower().endswith('calibre.exe'): name = os.path.basename(name) return error_dialog( self.gui, _('No associated program'), _('Windows will try to open %s with calibre itself' ' resulting in a duplicate in your calibre library. You' ' should install some program capable of viewing this' ' file format and tell Windows to use that program to open' ' files of this type.') % name, show=True) open_local_file(name) time.sleep(2) # User feedback finally: self.gui.unsetCursor()
def add_file(self): if current_container() is None: return error_dialog( self.gui, _('No open book'), _('You must first open a book to tweak, before trying to create new files' ' in it.'), show=True) self.commit_dirty_opf() d = NewFileDialog(self.gui) if d.exec_() != d.Accepted: return self.add_savepoint( _('Add file %s') % self.gui.elided_text(d.file_name)) c = current_container() data = d.file_data if d.using_template: data = data.replace(b'%CURSOR%', b'') try: c.add_file(d.file_name, data) except: self.rewind_savepoint() raise self.gui.file_list.build(c) self.gui.file_list.select_name(d.file_name) if c.opf_name in editors: editors[c.opf_name].replace_data(c.raw_data(c.opf_name)) mt = c.mime_map[d.file_name] syntax = syntax_from_mime(d.file_name, mt) if syntax: if d.using_template: self.edit_file(d.file_name, syntax, use_template=d.file_data.decode('utf-8')) else: self.edit_file(d.file_name, syntax)
def do_delete_user_category(self, category_name): ''' Delete the User category named category_name. Any leading '@' is removed ''' if category_name.startswith('@'): category_name = category_name[1:] db = self.library_view.model().db user_cats = db.new_api.pref('user_categories', {}) cat_keys = sorted(user_cats.keys(), key=sort_key) has_children = False found = False for k in cat_keys: if k == category_name: found = True has_children = len(user_cats[k]) elif k.startswith(category_name + '.'): has_children = True if not found: return error_dialog(self.tags_view, _('Delete User category'), _('%s is not a User category') % category_name, show=True) if has_children: if not question_dialog( self.tags_view, _('Delete User category'), _('%s contains items. Do you really ' 'want to delete it?') % category_name): return for k in cat_keys: if k == category_name: del user_cats[k] elif k.startswith(category_name + '.'): del user_cats[k] db.new_api.set_pref('user_categories', user_cats) self.tags_view.recount() db.new_api.clear_search_caches() self.user_categories_edited()
def initialize_db_stage2(self, db, tb): from calibre.db.legacy import LibraryDatabase if db is None and tb is not None: # DB Repair failed error_dialog(self.splash_screen, _('Repairing failed'), _('The database repair failed. Starting with ' 'a new empty library.'), det_msg=tb, show=True) if db is None: candidate = choose_dir( self.splash_screen, 'choose calibre library', _('Choose a location for your new calibre e-book library'), default_dir=get_default_library_path()) if not candidate: self.initialization_failed() try: self.library_path = candidate db = LibraryDatabase(candidate) except: error_dialog( self.splash_screen, _('Bad database location'), _('Bad database location %r. calibre will now quit.') % self.library_path, det_msg=traceback.format_exc(), show=True) self.initialization_failed() try: self.start_gui(db) except Exception: error_dialog( self.main, _('Startup error'), _('There was an error during {0} startup.' ' Parts of {0} may not function. Click Show details to learn more.' ).format(__appname__), det_msg=traceback.format_exc(), show=True)
def find_callback(self, found): d = self.dest_list text, flags, forwards = self.find_data if not found and text: if d.count() == 1: return error_dialog(self, _('No match found'), _('No match found for: %s') % text, show=True) delta = 1 if forwards else -1 current = unicode_type(d.currentItem().data(Qt.DisplayRole) or '') next_index = (d.currentRow() + delta) % d.count() next = unicode_type(d.item(next_index).data(Qt.DisplayRole) or '') msg = '<p>' + _( 'No matches for %(text)s found in the current file [%(current)s].' ' Do you want to search in the %(which)s file [%(next)s]?') msg = msg % dict(text=text, current=current, next=next, which=_('next') if forwards else _('previous')) if question_dialog(self, _('No match found'), msg): self.pending_search = self.find_next if forwards else self.find_previous d.setCurrentRow(next_index)
def add_formats_from_clipboard(self): ids = self._check_add_formats_ok() if not ids: return md = QApplication.instance().clipboard().mimeData() files_to_add = [] images = [] if md.hasUrls(): for url in md.urls(): if url.isLocalFile(): path = url.toLocalFile() if os.access(path, os.R_OK): mt = guess_type(path)[0] if mt and mt.startswith('image/'): images.append(path) else: files_to_add.append(path) if not files_to_add and not images: return error_dialog(self.gui, _('No files in clipboard'), _('No files have been copied to the clipboard'), show=True) if files_to_add: self._add_formats(files_to_add, ids) if images: if len(ids) > 1 and not question_dialog( self.gui, _('Are you sure?'), _('Are you sure you want to set the same' ' cover for all %d books?')%len(ids)): return with lopen(images[0], 'rb') as f: cdata = f.read() self.gui.current_db.new_api.set_cover({book_id: cdata for book_id in ids}) self.gui.refresh_cover_browser() m = self.gui.library_view.model() current = self.gui.library_view.currentIndex() m.current_changed(current, current)
def generate_catalog(self): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) < 2: rows = xrange(self.gui.library_view.model().rowCount( QModelIndex())) ids = map(self.gui.library_view.model().id, rows) if not ids: return error_dialog(self.gui, _('No books selected'), _('No books selected for catalog generation'), show=True) db = self.gui.library_view.model().db dbspec = {} for id in ids: dbspec[id] = {'ondevice': db.ondevice(id, index_is_id=True)} # Calling gui2.tools:generate_catalog() ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager, db) if ret is None: return func, args, desc, out, sync, title = ret fmt = os.path.splitext(out)[1][1:].upper() job = self.gui.job_manager.run_job(self.Dispatcher( self.catalog_generated), func, args=args, description=desc) job.catalog_file_path = out job.fmt = fmt job.catalog_sync, job.catalog_title = sync, title self.gui.status_bar.show_message(_('Generating %s catalog...') % fmt)
def export_key(self): if not self.listy.currentItem(): errmsg = "No keyfile selected to export. Highlight a keyfile first." r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(errmsg), show=True, show_copy_button=False) return keyname = str(self.listy.currentItem().text()) unique_dlg_name = PLUGIN_NAME + "export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory caption = "Save {0} File as...".format(self.key_type_name) filters = [("{0} Files".format(self.key_type_name), ["{0}".format(self.keyfile_ext)])] defaultname = "{0}.{1}".format(keyname, self.keyfile_ext) filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname) if filename: with open(filename, 'w') as fname: if self.binary_file: fname.write(self.plugin_keys[keyname].decode('hex')) elif self.json_file: fname.write(json.dumps(self.plugin_keys[keyname])) elif self.android_file: for key in self.plugin_keys[keyname]: fname.write(key) fname.write("\n") else: fname.write(self.plugin_keys[keyname])
def get_books_for_polishing(self): rows = [r.row() for r in self.gui.library_view.selectionModel().selectedRows()] if not rows or len(rows) == 0: d = error_dialog(self.gui, _('Cannot polish'), _('No books selected')) d.exec_() return None db = self.gui.library_view.model().db ans = (db.id(r) for r in rows) ans = self.get_supported_books(ans) for fmts in ans.itervalues(): for x in fmts: if x.startswith('ORIGINAL_'): from calibre.gui2.dialogs.confirm_delete import confirm if not confirm(_( 'One of the books you are polishing has an {0} format.' ' Polishing will use this as the source and overwrite' ' any existing {1} format. Are you sure you want to proceed?').format( x, x[len('ORIGINAL_'):]), 'confirm_original_polish', title=_('Are you sure?'), confirm_msg=_('Ask for this confirmation again')): return {} break return ans
def accept(self): self.actions = ac = {} saved_prefs = {} gprefs['polish_show_reports'] = bool(self.show_reports.isChecked()) something = False for action in self.all_actions: ac[action] = saved_prefs[action] = bool( getattr(self, 'opt_' + action).isChecked()) if ac[action]: something = True if ac['jacket'] and not ac['metadata']: if not question_dialog( self, _('Must update metadata'), _('You have selected the option to add metadata as ' 'a "book jacket". For this option to work, you ' 'must also select the option to update metadata in' ' the book files. Do you want to select it?')): return ac['metadata'] = saved_prefs['metadata'] = True self.opt_metadata.setChecked(True) if ac['jacket'] and ac['remove_jacket']: if not question_dialog( self, _('Add or remove jacket?'), _('You have chosen to both add and remove the metadata jacket.' ' This will result in the final book having no jacket. Is this' ' what you want?')): return if not something: return error_dialog( self, _('No actions selected'), _('You must select at least one action, or click Cancel.'), show=True) gprefs['polishing_settings'] = saved_prefs self.queue_files() return super(Polish, self).accept()
def do_config(self, checked=False, initial_plugin=None, close_after_initial=False): if self.gui.job_manager.has_jobs(): d = error_dialog( self.gui, _('Cannot configure'), _('Cannot configure while there are running jobs.')) d.exec() return if self.gui.must_restart_before_config: do_restart = show_restart_warning( _('Cannot configure before calibre is restarted.')) if do_restart: self.gui.quit(restart=True) return d = Preferences(self.gui, initial_plugin=initial_plugin, close_after_initial=close_after_initial) d.run_wizard_requested.connect(self.gui.run_wizard, type=Qt.ConnectionType.QueuedConnection) d.exec() if d.do_restart: self.gui.quit(restart=True)