Exemple #1
0
    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
Exemple #2
0
    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
Exemple #3
0
    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
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
 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()
Exemple #7
0
 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)
Exemple #8
0
 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()
Exemple #9
0
    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)
Exemple #10
0
    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
Exemple #11
0
    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()
Exemple #12
0
 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
Exemple #13
0
 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
Exemple #14
0
 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())
Exemple #15
0
    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()
Exemple #16
0
    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
Exemple #17
0
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
Exemple #19
0
 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
Exemple #20
0
    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()
Exemple #21
0
    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())
Exemple #22
0
 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)
Exemple #23
0
 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)
Exemple #24
0
 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)
Exemple #25
0
    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)
Exemple #26
0
 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)
Exemple #27
0
    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)
Exemple #28
0
 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)
Exemple #29
0
 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)
Exemple #30
0
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)
Exemple #32
0
    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)
Exemple #33
0
 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)))
Exemple #34
0
 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()
Exemple #35
0
 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 &lt;p&gt; or &lt;div&gt; tag.'
                   ),
                 show=True)
         tags = [tag]
     for tag in reversed(tags):
         set_style_property(tag, 'text-align', value, editor)
Exemple #36
0
    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)
Exemple #37
0
    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)
Exemple #38
0
 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)
Exemple #39
0
    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)
Exemple #40
0
 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
Exemple #41
0
    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())
Exemple #42
0
    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()
Exemple #44
0
 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()
Exemple #45
0
 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'))
Exemple #46
0
 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)
Exemple #48
0
 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()
Exemple #49
0
    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)
Exemple #50
0
    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()
Exemple #51
0
    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)
Exemple #52
0
 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()
Exemple #53
0
    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)
Exemple #54
0
    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)
Exemple #55
0
 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)
Exemple #56
0
    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)
Exemple #57
0
 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])
Exemple #58
0
 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
Exemple #59
0
 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()
Exemple #60
0
 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)