Exemplo n.º 1
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()
Exemplo n.º 2
0
    def confirm_quit(self):
        if self.job_manager.has_jobs():
            msg = _("There are active jobs. Are you sure you want to quit?")
            if self.job_manager.has_device_jobs():
                msg = (
                    "<p>"
                    + __appname__
                    + _(
                        """ is communicating with the device!<br>
                      Quitting may cause corruption on the device.<br>
                      Are you sure you want to quit?"""
                    )
                    + "</p>"
                )

            if not question_dialog(self, _("Active jobs"), msg):
                return False
        from calibre.db.delete_service import has_jobs

        if has_jobs():
            msg = _(
                "Some deleted books are still being moved to the Recycle "
                "Bin, if you quit now, they will be left behind. Are you "
                "sure you want to quit?"
            )
            if not question_dialog(self, _("Active jobs"), msg):
                return False

        return True
Exemplo n.º 3
0
    def confirm_quit(self):
        if self.job_manager.has_jobs():
            msg = _('There are active jobs. Are you sure you want to quit?')
            if self.job_manager.has_device_jobs():
                msg = '<p>'+__appname__ + \
                      _(''' is communicating with the device!<br>
                      Quitting may cause corruption on the device.<br>
                      Are you sure you want to quit?''')+'</p>'

            if not question_dialog(self, _('Active jobs'), msg):
                return False

        if self.proceed_question.questions:
            msg = _('There are library updates waiting. Are you sure you want to quit?')
            if not question_dialog(self, _('Library Updates Waiting'), msg):
                return False

        from calibre.db.delete_service import has_jobs
        if has_jobs():
            msg = _('Some deleted books are still being moved to the Recycle '
                    'Bin, if you quit now, they will be left behind. Are you '
                    'sure you want to quit?')
            if not question_dialog(self, _('Active jobs'), msg):
                return False

        return True
Exemplo n.º 4
0
    def accept(self):
        n = unicode(self.vl_name.currentText()).strip()
        if not n:
            error_dialog(self.gui, _("No name"), _("You must provide a name for the new virtual library"), show=True)
            return

        if n.startswith("*"):
            error_dialog(self.gui, _("Invalid name"), _('A virtual library name cannot begin with "*"'), show=True)
            return

        if n in self.existing_names and n != self.editing:
            if not question_dialog(
                self.gui,
                _("Name already in use"),
                _("That name is already in use. Do you want to replace it " "with the new search?"),
                default_yes=False,
            ):
                return

        v = unicode(self.vl_text.text()).strip()
        if not v:
            error_dialog(
                self.gui,
                _("No search string"),
                _("You must provide a search to define the new virtual library"),
                show=True,
            )
            return

        try:
            db = self.gui.library_view.model().db
            recs = db.data.search_getting_ids("", v, use_virtual_library=False)
        except ParseException as e:
            error_dialog(
                self.gui, _("Invalid search"), _("The search in the search box is not valid"), det_msg=e.msg, show=True
            )
            return

        if not recs and not question_dialog(
            self.gui,
            _("Search found no books"),
            _(
                "The search found no books, so the virtual library "
                "will be empty. Do you really want to use that search?"
            ),
            default_yes=False,
        ):
            return

        self.library_name = n
        self.library_search = v
        QDialog.accept(self)
Exemplo n.º 5
0
    def initialize_db(self):
        from calibre.db.legacy import LibraryDatabase
        db = None
        try:
            db = LibraryDatabase(self.library_path)
        except apsw.Error:
            with self.app:
                self.hide_splash_screen()
                repair = question_dialog(self.splash_screen, _('Corrupted database'),
                        _('The library database at %s appears to be corrupted. Do '
                        'you want calibre to try and rebuild it automatically? '
                        'The rebuild may not be completely successful. '
                        'If you say No, a new empty calibre library will be created.')
                        % force_unicode(self.library_path, filesystem_encoding),
                        det_msg=traceback.format_exc()
                        )
            if repair:
                if repair_library(self.library_path):
                    db = LibraryDatabase(self.library_path)
        except:
            self.show_error(_('Bad database location'),
                    _('Bad database location %r. Will start with '
                    ' a new, empty calibre library')%self.library_path,
                    det_msg=traceback.format_exc())

        self.initialize_db_stage2(db, None)
Exemplo n.º 6
0
 def check_dirtied(self):
     dirtied = {name for name, ed in editors.iteritems() if ed.is_modified}
     if not dirtied:
         return True
     return question_dialog(self.gui, _('Unsaved changes'), _(
         'You have unsaved changes in the files %s. If you proceed,'
         ' you will lose them. Proceed anyway?') % ', '.join(dirtied))
Exemplo n.º 7
0
    def delete_row(self):
        if self.DEBUG:
            print("%s:delete_row()" % self.objectName())

        self.setFocus()
        rows = self.last_rows_selected
        if len(rows) == 0:
            return

        first = rows[0].row() + 1
        last = rows[-1].row() + 1

        first_rule_name = unicode(self.cellWidget(first-1,self.COLUMNS['NAME']['ordinal']).text()).strip()
        message = _("Are you sure you want to delete '%s'?") % (first_rule_name)
        if len(rows) > 1:
            message = _('Are you sure you want to delete rules #%(first)d-%(last)d?') % dict(first=first, last=last)
        if not question_dialog(self, _('Delete Rule'), message, show_copy_button=False):
            return
        first_sel_row = self.currentRow()
        for selrow in reversed(rows):
            self.removeRow(selrow.row())
        if first_sel_row < self.rowCount():
            self.select_and_scroll_to_row(first_sel_row)
        elif self.rowCount() > 0:
            self.select_and_scroll_to_row(first_sel_row - 1)
Exemplo n.º 8
0
    def initialize_db(self):
        from calibre.db.legacy import LibraryDatabase
        db = None
        self.timed_print('Initializing db...')
        try:
            db = LibraryDatabase(self.library_path)
        except apsw.Error:
            with self.app:
                self.hide_splash_screen()
                repair = question_dialog(self.splash_screen, _('Corrupted database'),
                        _('The library database at %s appears to be corrupted. Do '
                        'you want calibre to try and rebuild it automatically? '
                        'The rebuild may not be completely successful. '
                        'If you say No, a new empty calibre library will be created.')
                        % force_unicode(self.library_path, filesystem_encoding),
                        det_msg=traceback.format_exc()
                        )
            if repair:
                if iswindows:
                    # On some windows systems the existing db file gets locked
                    # by something when running restore from the main process.
                    # So run the restore in a separate process.
                    windows_repair(self.library_path)
                    self.app.quit()
                    return
                if repair_library(self.library_path):
                    db = LibraryDatabase(self.library_path)
        except:
            self.show_error(_('Bad database location'),
                    _('Bad database location %r. Will start with '
                    ' a new, empty calibre library')%self.library_path,
                    det_msg=traceback.format_exc())

        self.initialize_db_stage2(db, None)
Exemplo n.º 9
0
 def set_email_settings(self, to_set):
     from_ = unicode(self.email_from.text()).strip()
     if to_set and not from_:
         error_dialog(self, _('Bad configuration'),
                      _('You must set the From email address')).exec_()
         return False
     username = unicode(self.relay_username.text()).strip()
     password = unicode(self.relay_password.text()).strip()
     host = unicode(self.relay_host.text()).strip()
     enc_method = ('TLS' if self.relay_tls.isChecked() else 'SSL'
             if self.relay_ssl.isChecked() else 'NONE')
     if host:
         # Validate input
         if ((username and not password) or (not username and password)):
             error_dialog(self, _('Bad configuration'),
                         _('You must either set both the username <b>and</b> password for '
                         'the mail server or no username and no password at all.')).exec_()
             return False
         if not (username and password) and not question_dialog(self,
                 _('Are you sure?'),
             _('No username and password set for mailserver. Most '
                 ' mailservers need a username and password. Are you sure?')):
                 return False
     conf = smtp_prefs()
     conf.set('from_', from_)
     conf.set('relay_host', host if host else None)
     conf.set('relay_port', self.relay_port.value())
     conf.set('relay_username', username if username else None)
     conf.set('relay_password', hexlify(password.encode('utf-8')))
     conf.set('encryption', enc_method)
     return True
Exemplo n.º 10
0
    def do_tag_item_delete(self, category, item_id, orig_name):
        '''
        Delete an item from some category.
        '''
        if not question_dialog(self.tags_view,
                    title=_('Delete item'),
                    msg='<p>'+
                    _('%s will be deleted from all books. Are you sure?')
                                %orig_name,
                    skip_dialog_name='tag_item_delete',
                    skip_dialog_msg=_('Show this confirmation again')):
            return
        db = self.current_db

        if category == 'tags':
            delete_func = db.delete_tag_using_id
        elif category == 'series':
            delete_func = db.delete_series_using_id
        elif category == 'publisher':
            delete_func = db.delete_publisher_using_id
        else:  # must be custom
            cc_label = db.field_metadata[category]['label']
            delete_func = partial(db.delete_custom_item_using_id, label=cc_label)
        m = self.tags_view.model()
        if delete_func:
            delete_func(item_id)
            m.delete_item_from_all_user_categories(orig_name, category)

        # Clean up the library view
        self.do_tag_item_renamed()
        self.tags_view.recount()
Exemplo n.º 11
0
 def setData(self, index, val, role):
     try:
         plugin = self.plugins[index.row()]
     except:
         return False
     col = index.column()
     ret = False
     if col == 0 and role == Qt.CheckStateRole:
         val, ok = val.toInt()
         if ok:
             if val == Qt.Checked and 'Douban' in plugin.name:
                 if not question_dialog(self.gui_parent,
                     _('Are you sure?'), '<p>'+
                     _('This plugin is useful only for <b>Chinese</b>'
                         ' language books. It can return incorrect'
                         ' results for books in English. Are you'
                         ' sure you want to enable it?'),
                     show_copy_button=False):
                     return ret
             self.enabled_overrides[plugin] = val
             ret = True
     if col == 1 and role == Qt.EditRole:
         val, ok = val.toInt()
         if ok:
             self.cover_overrides[plugin] = val
             ret = True
     if ret:
         self.dataChanged.emit(index, index)
     return ret
Exemplo n.º 12
0
    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
Exemplo n.º 13
0
 def validate_import(self):
     if self.import_panel.stack.currentIndex() == 0:
         error_dialog(self, _('No folder selected'), _(
             'You must select a folder containing the previously exported data that you wish to import'), show=True)
         return False
     else:
         blanks = []
         for w in self.imported_lib_widgets:
             newloc = w.path
             if not newloc:
                 blanks.append(w.lpath)
                 continue
             if iswindows and len(newloc) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT:
                 error_dialog(self, _('Too long'),
                     _('Path to library ({0}) too long. Must be less than'
                     ' {1} characters.').format(newloc, LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT), show=True)
                 return False
             if not os.path.isdir(newloc):
                 error_dialog(self, _('Not a folder'), _('%s is not a folder')%newloc, show=True)
                 return False
             if os.listdir(newloc):
                 error_dialog(self, _('Folder not empty'), _('%s is not an empty folder')%newloc, show=True)
                 return False
         if blanks:
             if len(blanks) == len(self.imported_lib_widgets):
                 error_dialog(self, _('No libraries selected'), _(
                     'You must specify the location for at least one library'), show=True)
                 return False
             if not question_dialog(self, _('Some libraries ignored'), _(
                     'You have chosen not to import some libraries. Proceed anyway?')):
                 return False
     return True
Exemplo n.º 14
0
 def load(self):
     files = choose_files(
         self,
         "recipe loader dialog",
         _("Choose a recipe file"),
         filters=[(_("Recipes"), [".py", ".recipe"])],
         all_files=False,
         select_only_single_file=True,
     )
     if files:
         file = files[0]
         try:
             profile = open(file, "rb").read().decode("utf-8")
             title = compile_recipe(profile).title
         except Exception as err:
             error_dialog(self, _("Invalid input"), _("<p>Could not create recipe. Error:<br>%s") % str(err)).exec_()
             return
         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()
Exemplo n.º 15
0
    def initialize_db(self):
        from calibre.db.legacy import LibraryDatabase

        db = None
        try:
            db = LibraryDatabase(self.library_path)
        except apsw.Error:
            repair = question_dialog(
                self.splash_screen,
                _("Corrupted database"),
                _(
                    "The library database at %s appears to be corrupted. Do "
                    "you want calibre to try and rebuild it automatically? "
                    "The rebuild may not be completely successful. "
                    "If you say No, a new empty calibre library will be created."
                )
                % force_unicode(self.library_path, filesystem_encoding),
                det_msg=traceback.format_exc(),
            )
            if repair:
                if repair_library(self.library_path):
                    db = LibraryDatabase(self.library_path)
        except:
            error_dialog(
                self.splash_screen,
                _("Bad database location"),
                _("Bad database location %r. Will start with " " a new, empty calibre library") % self.library_path,
                det_msg=traceback.format_exc(),
                show=True,
            )

        self.initialize_db_stage2(db, None)
Exemplo n.º 16
0
    def count_statistics(self, book_ids, statistics_to_run, use_goodreads=False):
        '''
        This function is designed to be called from other plugins
        Note that the statistics functions can only be used if a
        custom column has been configured by the user first.

          book_ids - list of calibre book ids to run the statistics against

          statistics_to_run - list of statistic names to be run. Possible values:
              'PageCount', 'WordCount', 'FleschReading', 'FleschGrade', 'GunningFog'

          use_goodreads - only applies to PageCount, whether to retrieve from
                          Goodreads rather than using an estimation algorithm.
                          Requires each book to have a goodreads identifier.
        '''
        if statistics_to_run is None or len(statistics_to_run) == 0:
            print('Page count called but neither page nor word count requested')
            return

        # Verify we have a custom column configured to store the page/word count in
        any_valid, statistics_cols_map = self._get_column_validity(statistics_to_run)
        if (not any_valid):
            if not question_dialog(self.gui, 'Configure plugin', '<p>'+
                'You must specify custom column(s) first. Do you want to configure this now?',
                show_copy_button=False):
                return
            self.show_configuration()
            return

        self._do_count_pages(book_ids, statistics_cols_map, use_goodreads)
Exemplo n.º 17
0
def restore_database(db, parent=None):
    if not question_dialog(parent, _('Are you sure?'), '<p>'+
            _('Your list of books, with all their metadata is '
                'stored in a single file, called a database. '
                'In addition, metadata for each individual '
                'book is stored in that books\' folder, as '
                'a backup.'
                '<p>This operation will rebuild '
                'the database from the individual book '
                'metadata. This is useful if the '
                'database has been corrupted and you get a '
                'blank list of books.'
                '<p>Do you want to restore the database?')):
        return False
    db.close()
    d = DBRestore(parent, db.library_path)
    d.exec_()
    r = d.restorer
    d.restorer = None
    if d.rejected:
        return True
    if r.tb is not None:
        error_dialog(parent, _('Failed'),
        _('Restoring database failed, click Show details to see details'),
        det_msg=r.tb, show=True)
    else:
        _show_success_msg(r, parent=parent)
    return True
Exemplo n.º 18
0
 def remove_vl_triggered(self, name=None):
     if not question_dialog(self, _('Are you sure?'),
                  _('Are you sure you want to remove '
                    'the virtual library {0}').format(name),
                     default_yes=False):
         return
     self._remove_vl(name, reapply=True)
Exemplo n.º 19
0
    def rename_key(self):
        if not self.listy.currentItem():
            errmsg = u"No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
            r = error_dialog(
                None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), _(errmsg), show=True, show_copy_button=False
            )
            return

        d = RenameKeyDialog(self)
        d.exec_()

        if d.result() != d.Accepted:
            # rename cancelled or moot.
            return
        keyname = unicode(self.listy.currentItem().text())
        if not question_dialog(
            self,
            "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION),
            u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(
                keyname, d.key_name, self.key_type_name
            ),
            show_copy_button=False,
            default_yes=False,
        ):
            return
        self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
        del self.plugin_keys[keyname]

        self.listy.clear()
        self.populate_list()
Exemplo n.º 20
0
    def add_formats(self, *args):
        if self.gui.stack.currentIndex() != 0:
            return
        view = self.gui.library_view
        rows = view.selectionModel().selectedRows()
        if not rows:
            return error_dialog(self.gui, _('No books selected'),
                    _('Cannot add files as no books are selected'), show=True)
        ids = [view.model().id(r) for r in rows]

        if len(ids) > 1 and not question_dialog(self.gui,
                _('Are you sure'),
            _('Are you sure you want to add the same'
                ' files to all %d books? If the format'
                ' already exists for a book, it will be replaced.')%len(ids)):
                return

        books = choose_files(self.gui, 'add formats dialog dir',
                _('Select book files'), filters=get_filters())
        if not books:
            return

        db = view.model().db
        for id_ in ids:
            for fpath in books:
                fmt = os.path.splitext(fpath)[1][1:].upper()
                if fmt:
                    db.add_format_with_hooks(id_, fmt, fpath, index_is_id=True,
                        notify=True)
        current_idx = self.gui.library_view.currentIndex()
        if current_idx.isValid():
            view.model().current_changed(current_idx, current_idx)
Exemplo n.º 21
0
 def confirm_delete(self, spine_items, other_names):
     spine_names = {name for name, remove in spine_items if remove}
     if not question_dialog(self, _('Are you sure?'), _(
             'Are you sure you want to delete the selected files?'), det_msg='\n'.join(spine_names | other_names)):
         return
     self.delete_requested.emit(spine_items, other_names)
     QTimer.singleShot(10, self.refresh)
Exemplo n.º 22
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.prefs.get('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()
Exemplo n.º 23
0
    def _add_formats(self, paths, ids):
        if len(ids) > 1 and not question_dialog(
                self.gui,
                _('Are you sure?'),
                _('Are you sure you want to add the same'
                  ' files to all %d books? If the format'
                  ' already exists for a book, it will be replaced.')%len(ids)):
            return

        db = self.gui.current_db
        if len(ids) == 1:
            formats = db.formats(ids[0], index_is_id=True)
            if formats:
                formats = {x.upper() for x in formats.split(',')}
                nformats = {f.rpartition('.')[-1].upper() for f in paths}
                override = formats.intersection(nformats)
                if override:
                    title = db.title(ids[0], index_is_id=True)
                    msg = ngettext(
                        'The {0} format will be replaced in the book {1}. Are you sure?',
                        'The {0} formats will be replaced in the book {1}. Are you sure?',
                        len(override)).format(', '.join(override), title)
                    if not confirm(msg, 'confirm_format_override_on_add', title=_('Are you sure?'), parent=self.gui):
                        return

        fmt_map = {os.path.splitext(fpath)[1][1:].upper():fpath for fpath in paths}

        for id_ in ids:
            for fmt, fpath in iteritems(fmt_map):
                if fmt:
                    db.add_format_with_hooks(id_, fmt, fpath, index_is_id=True,
                        notify=True)
        current_idx = self.gui.library_view.currentIndex()
        if current_idx.isValid():
            self.gui.library_view.model().current_changed(current_idx, current_idx)
Exemplo n.º 24
0
    def delete_tags(self, item=None):
        confirms, deletes = [], []
        items = self.available_tags.selectedItems() if item is None else [item]
        if not items:
            error_dialog(self, 'No tags selected', 'You must select at least one tag from the list of Available tags.').exec_()
            return
        pos = self.available_tags.verticalScrollBar().value()
        for item in items:
            used = self.db.is_tag_used(unicode(item.text())) \
                if self.key is None else \
                self.db.is_item_used_in_multiple(unicode(item.text()), label=self.key)
            if used:
                confirms.append(item)
            else:
                deletes.append(item)
        if confirms:
            ct = ', '.join([unicode(item.text()) for item in confirms])
            if question_dialog(self, _('Are your sure?'),
                '<p>'+_('The following tags are used by one or more books. '
                    'Are you certain you want to delete them?')+'<br>'+ct):
                deletes += confirms

        for item in deletes:
            if self.key is None:
                self.db.delete_tag(unicode(item.text()))
            else:
                bks = self.db.delete_item_from_multiple(unicode(item.text()),
                                                        label=self.key)
                self.db.refresh_ids(bks)
            self.available_tags.takeItem(self.available_tags.row(item))
        self.available_tags.verticalScrollBar().setValue(pos)
Exemplo n.º 25
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()
Exemplo n.º 26
0
 def check_opf_dirtied(self):
     c = current_container()
     if c.opf_name in editors and editors[c.opf_name].is_modified:
         return question_dialog(self.gui, _('Unsaved changes'), _(
             'You have unsaved changes in %s. If you proceed,'
             ' you will lose them. Proceed anyway?') % c.opf_name)
     return True
Exemplo n.º 27
0
 def commit(self):
     path = unicode(self.opt_auto_add_path.text()).strip()
     if path != gprefs['auto_add_path']:
         if path:
             path = os.path.abspath(path)
             self.opt_auto_add_path.setText(path)
             if not os.path.isdir(path):
                 error_dialog(self, _('Invalid folder'),
                         _('You must specify an existing folder as your '
                             'auto-add folder. %s does not exist.')%path,
                         show=True)
                 raise AbortCommit('invalid auto-add folder')
             if not os.access(path, os.R_OK|os.W_OK):
                 error_dialog(self, _('Invalid folder'),
                         _('You do not have read/write permissions for '
                             'the folder: %s')%path, show=True)
                 raise AbortCommit('invalid auto-add folder')
             if not question_dialog(self, _('Are you sure?'),
                     _('<b>WARNING:</b> Any files you place in %s will be '
                         'automatically deleted after being added to '
                         'calibre. Are you sure?')%path):
                 return
     pattern = self.filename_pattern.commit()
     prefs['filename_pattern'] = pattern
     fmts = self.current_blocked_auto_formats
     old = gprefs['blocked_auto_formats']
     changed = set(fmts) != set(old)
     if changed:
         gprefs['blocked_auto_formats'] = self.current_blocked_auto_formats
     ret = ConfigWidgetBase.commit(self)
     return changed or ret
Exemplo n.º 28
0
    def initialize_db(self):
        from calibre.db import get_db_loader
        db = None
        self.db_class, errs = get_db_loader()
        try:
            db = self.db_class(self.library_path)
        except errs:
            repair = question_dialog(self.splash_screen, _('Corrupted database'),
                    _('The library database at %s appears to be corrupted. Do '
                    'you want calibre to try and rebuild it automatically? '
                    'The rebuild may not be completely successful. '
                    'If you say No, a new empty calibre library will be created.')
                    % force_unicode(self.library_path, filesystem_encoding),
                    det_msg=traceback.format_exc()
                    )
            if repair:
                if repair_library(self.library_path):
                    db = self.db_class(self.library_path)
        except:
            error_dialog(self.splash_screen, _('Bad database location'),
                    _('Bad database location %r. Will start with '
                    ' a new, empty calibre library')%self.library_path,
                    det_msg=traceback.format_exc(), show=True)

        self.initialize_db_stage2(db, None)
Exemplo n.º 29
0
    def add_builtin_recipe(self):
        from calibre.web.feeds.recipes.collection import \
            get_builtin_recipe_collection, get_builtin_recipe_by_id
        from PyQt5.Qt import QDialog, QVBoxLayout, QListWidgetItem, \
                QListWidget, QDialogButtonBox, QSize

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

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

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

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

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

        self.clear()
Exemplo n.º 30
0
 def restore_defaults(self):
     if self.current_theme is not None:
         if not question_dialog(self, _('Are you sure?'), _(
                 'Are you sure you want to remove the <b>%s</b> icon theme'
                 ' and return to the stock icons?') % self.current_theme):
             return
     self.commit_changes = remove_icon_theme
     Dialog.accept(self)
Exemplo n.º 31
0
    def check_library(self):
        from calibre.gui2.dialogs.check_library import CheckLibraryDialog, DBCheck
        self.gui.library_view.save_state()
        m = self.gui.library_view.model()
        m.stop_metadata_backup()
        db = m.db
        db.prefs.disable_setting = True

        d = DBCheck(self.gui, db)
        d.start()
        try:
            d.conn.close()
        except:
            pass
        d.break_cycles()
        self.gui.library_moved(db.library_path, call_close=not
                d.closed_orig_conn)
        if d.rejected:
            return
        if d.error is None:
            if not question_dialog(self.gui, _('Success'),
                    _('Found no errors in your calibre library database.'
                        ' Do you want calibre to check if the files in your '
                        ' library match the information in the database?')):
                return
        else:
            return error_dialog(self.gui, _('Failed'),
                    _('Database integrity check failed, click Show details'
                        ' for details.'), show=True, det_msg=d.error[1])

        self.gui.status_bar.show_message(
                _('Starting library scan, this may take a while'))
        try:
            QCoreApplication.processEvents()
            d = CheckLibraryDialog(self.gui, m.db)

            if not d.do_exec():
                info_dialog(self.gui, _('No problems found'),
                        _('The files in your library match the information '
                        'in the database.'), show=True)
        finally:
            self.gui.status_bar.clear_message()
Exemplo n.º 32
0
    def delete_tags(self):
        confirms, deletes = [], []
        row_indices = list(self.available_tags.selectionModel().selectedRows())

        if not row_indices:
            error_dialog(
                self, _('No tags selected'),
                _('You must select at least one tag from the list of Available tags.'
                  )).exec()
            return
        if not confirm(
                _('Deleting tags is done immediately and there is no undo.'),
                'tag_editor_delete'):
            return
        pos = self.available_tags.verticalScrollBar().value()
        for ri in row_indices:
            tag = ri.data()
            used = self.db.is_tag_used(tag) \
                if self.key is None else \
                self.db.is_item_used_in_multiple(tag, label=self.key)
            if used:
                confirms.append(ri)
            else:
                deletes.append(ri)
        if confirms:
            ct = ', '.join(item.data() for item in confirms)
            if question_dialog(
                    self, _('Are your sure?'), '<p>' +
                    _('The following tags are used by one or more books. '
                      'Are you certain you want to delete them?') + '<br>' +
                    ct):
                deletes += confirms

        for item in sorted(deletes, key=lambda r: r.row(), reverse=True):
            tag = item.data()
            if self.key is None:
                self.db.delete_tag(tag)
            else:
                bks = self.db.delete_item_from_multiple(tag, label=self.key)
                self.db.refresh_ids(bks)
            self.available_tags.model().removeRows(item.row(), 1)
        self.available_tags.verticalScrollBar().setValue(pos)
Exemplo n.º 33
0
 def delete_requested(self, name, location):
     loc = location.replace('/', os.sep)
     if not question_dialog(
             self.gui,
             _('Library removed'),
             _('The library %s has been removed from calibre. '
               'The files remain on your computer, if you want '
               'to delete them, you will have to do so manually.') %
         ('<code>%s</code>' % loc),
             override_icon='dialog_information.png',
             yes_text=_('&OK'),
             no_text=_('&Undo'),
             yes_icon='ok.png',
             no_icon='edit-undo.png'):
         return
     self.stats.remove(location)
     self.build_menus()
     self.gui.iactions['Copy To Library'].build_menus()
     if os.path.exists(loc):
         open_local_file(loc)
Exemplo n.º 34
0
    def is_ok_to_add_empty_formats(self):
        if self.gui.stack.currentIndex() != 0:
            return

        view = self.gui.library_view
        rows = view.selectionModel().selectedRows()
        if not rows:
            return error_dialog(self.gui, _('No books selected'),
                    _('Cannot add files as no books are selected'), show=True)

        ids = [view.model().id(r) for r in rows]

        if len(ids) > 1 and not question_dialog(
                self.gui,
                _('Are you sure?'),
                _('Are you sure you want to add the same'
                  ' empty file to all %d books? If the format'
                  ' already exists for a book, it will be replaced.')%len(ids)):
            return
        return True
Exemplo n.º 35
0
    def s_r_remove_query(self, *args):
        if self.query_field.currentIndex() == 0:
            return

        if not question_dialog(self, _("Delete saved search/replace"),
                _("The selected saved search/replace will be deleted. "
                    "Are you sure?")):
            return

        item_id = self.query_field.currentIndex()
        item_name = unicode(self.query_field.currentText())

        self.query_field.blockSignals(True)
        self.query_field.removeItem(item_id)
        self.query_field.blockSignals(False)
        self.query_field.setCurrentIndex(0)

        if item_name in self.queries.keys():
            del(self.queries[item_name])
            self.queries.commit()
Exemplo n.º 36
0
 def del_custcol(self):
     idx = self.opt_columns.currentRow()
     if idx < 0:
         return error_dialog(self, '', _('You must select a column to delete it'),
                 show=True)
     col = unicode(self.opt_columns.item(idx).data(Qt.UserRole) or '')
     if col not in self.custcols:
         return error_dialog(self, '',
                 _('The selected column is not a custom column'), show=True)
     if not question_dialog(self, _('Are you sure?'),
         _('Do you really want to delete column %s and all its data?') %
         self.custcols[col]['name'], show_copy_button=False):
         return
     self.opt_columns.item(idx).setCheckState(False)
     self.opt_columns.takeItem(idx)
     if self.custcols[col]['colnum'] is None:
         del self.custcols[col] # A newly-added column was deleted
     else:
         self.custcols[col]['*deleteme'] = True
     self.changed_signal.emit()
Exemplo n.º 37
0
 def validate(self):
     '''
     Do a syntax check on the format string. Doing a semantic check
     (verifying that the fields exist) is not useful in the presence of
     custom fields, because they may or may not exist.
     '''
     tmpl = preprocess_template(self.opt_template.text())
     try:
         t = validation_formatter.validate(tmpl)
         if t.find(validation_formatter._validation_string) < 0:
             return question_dialog(
                 self, _('Constant template'),
                 _('The template contains no {fields}, so all '
                   'books will have the same name. Is this OK?'))
     except Exception as err:
         error_dialog(self, _('Invalid template'),
                 '<p>'+_('The template %s is invalid:')%tmpl + \
                 '<br>'+str(err), show=True)
         return False
     return True
Exemplo n.º 38
0
def get_customization(action, name, parent):
    ans = CUSTOMIZATION.copy()
    try:
        if action == 'remove_unused_css':
            customize_remove_unused_css(name, parent, ans)
        elif action == 'upgrade_book':
            ans['remove_ncx'] = tprefs['remove_ncx'] = question_dialog(
                parent, _('Remove NCX ToC file'),
                _('Remove the legacy Table of Contents in NCX form?'),
                _('This form of Table of Contents is superseded by the new HTML based Table of Contents.'
                  ' Leaving it behind is useful only if you expect this book to be read on very'
                  ' old devices that lack proper support for EPUB 3'),
                skip_dialog_name='edit-book-remove-ncx',
                skip_dialog_msg=_('Ask this question again in the future'),
                skip_dialog_skipped_value=tprefs['remove_ncx'],
                yes_text=_('Remove NCX'), no_text=_('Keep NCX')
            )
    except Abort:
        return None
    return ans
Exemplo n.º 39
0
    def delete_tags(self):
        deletes = self.table.selectedItems()
        if not deletes:
            error_dialog(self, _('No items selected'),
                         _('You must select at least one item from the list.')).exec_()
            return
        ct = ', '.join([unicode(item.text()) for item in deletes])
        if not question_dialog(self, _('Are you sure?'),
            '<p>'+_('Are you sure you want to delete the following items?')+'<br>'+ct):
            return
        row = self.table.row(deletes[0])
        for item in deletes:
            id = int(item.data(Qt.UserRole))
            self.to_delete.add(id)
            self.table.removeRow(self.table.row(item))

        if row >= self.table.rowCount():
            row = self.table.rowCount() - 1
        if row >= 0:
            self.table.scrollToItem(self.table.item(row, 0))
Exemplo n.º 40
0
def convert_existing(parent, db, book_ids, output_format):  # {{{
    already_converted_ids = []
    already_converted_titles = []
    for book_id in book_ids:
        if db.has_format(book_id, output_format, index_is_id=True):
            already_converted_ids.append(book_id)
            already_converted_titles.append(
                db.get_metadata(book_id, True).title)

    if already_converted_ids:
        if not question_dialog(
                parent,
                _('Convert existing'),
                _('The following books have already been converted to the %s format. '
                  'Do you wish to reconvert them?') % output_format.upper(),
                det_msg='\n'.join(already_converted_titles),
                skip_dialog_name='confirm_bulk_reconvert'):
            book_ids = [x for x in book_ids if x not in already_converted_ids]

    return book_ids
Exemplo n.º 41
0
 def accept(self, *args):
     tags = unicode(self.add_tags.text()).strip().split(',')
     tags = list(filter(None, [x.strip() for x in tags]))
     gprefs['add from ISBN tags'] = tags
     self.set_tags = tags
     bad = set()
     for line in unicode(self.isbn_box.toPlainText()).strip().splitlines():
         line = line.strip()
         if not line:
             continue
         parts = line.split('>>')
         if len(parts) > 2:
             parts = [parts[0] + '>>'.join(parts[1:])]
         parts = [x.strip() for x in parts]
         if not parts[0]:
             continue
         isbn = check_isbn(parts[0])
         if isbn is not None:
             isbn = isbn.upper()
             if isbn not in self.isbns:
                 self.isbns.append(isbn)
                 book = {'isbn': isbn, 'path': None}
                 if len(parts) > 1 and parts[1] and \
                     os.access(parts[1], os.R_OK) and os.path.isfile(parts[1]):
                     book['path'] = parts[1]
                 self.books.append(book)
         else:
             bad.add(parts[0])
     if bad:
         if self.books:
             if not question_dialog(self, _('Some invalid ISBNs'),
                 _('Some of the ISBNs you entered were invalid. They will'
                     ' be ignored. Click Show Details to see which ones.'
                     ' Do you want to proceed?'), det_msg='\n'.join(bad),
                 show_copy_button=True):
                 return
         else:
             return error_dialog(self, _('All invalid ISBNs'),
                     _('All the ISBNs you entered were invalid. No books'
                         ' can be added.'), show=True)
     QDialog.accept(self, *args)
Exemplo n.º 42
0
    def delete_tags(self, item=None):
        confirms, deletes = [], []
        items = self.available_tags.selectedItems() if item is None else [item]
        if not items:
            error_dialog(
                self, 'No tags selected',
                'You must select at least one tag from the list of Available tags.'
            ).exec_()
            return
        if not confirm(
                _('Deleting tags is done immediately and there is no undo.'),
                'tag_editor_delete'):
            return
        pos = self.available_tags.verticalScrollBar().value()
        for item in items:
            used = self.db.is_tag_used(unicode_type(item.text())) \
                if self.key is None else \
                self.db.is_item_used_in_multiple(unicode_type(item.text()), label=self.key)
            if used:
                confirms.append(item)
            else:
                deletes.append(item)
        if confirms:
            ct = ', '.join([unicode_type(item.text()) for item in confirms])
            if question_dialog(
                    self, _('Are your sure?'), '<p>' +
                    _('The following tags are used by one or more books. '
                      'Are you certain you want to delete them?') + '<br>' +
                    ct):
                deletes += confirms

        for item in deletes:
            if self.key is None:
                self.db.delete_tag(unicode_type(item.text()))
            else:
                bks = self.db.delete_item_from_multiple(unicode_type(
                    item.text()),
                                                        label=self.key)
                self.db.refresh_ids(bks)
            self.available_tags.takeItem(self.available_tags.row(item))
        self.available_tags.verticalScrollBar().setValue(pos)
Exemplo n.º 43
0
 def kill_job(self, *args):
     indices = [
         self.proxy_model.mapToSource(index)
         for index in self.jobs_view.selectionModel().selectedRows()
     ]
     indices = [i for i in indices if i.isValid()]
     jobs = self.model.rows_to_jobs([index.row() for index in indices])
     if not jobs:
         return error_dialog(self,
                             _('No job'),
                             _('No job selected'),
                             show=True)
     if question_dialog(
             self, _('Are you sure?'),
             ngettext('Do you really want to stop the selected job?',
                      'Do you really want to stop all the selected jobs?',
                      len(jobs))):
         if len(jobs) > 1:
             self.model.kill_multiple_jobs(jobs, self)
         else:
             self.model.kill_job(jobs[0], self)
Exemplo n.º 44
0
    def rename_key(self):
        if not self.listy.currentItem():
            errmsg = "No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
            r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
                                    _(errmsg), show=True, show_copy_button=False)
            return

        d = RenameKeyDialog(self)
        d.exec_()

        if d.result() != d.Accepted:
            # rename cancelled or moot.
            return
        keyname = str(self.listy.currentItem().text())
        if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
            return
        self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
        del self.plugin_keys[keyname]

        self.listy.clear()
        self.populate_list()
Exemplo n.º 45
0
 def _rename_tag(self, item):
     if item is None:
         error_dialog(self, _('No item selected'),
                      _('You must select one item from the list of Available items.')).exec_()
         return
     for col_zero_item in self.table.selectedItems():
         if col_zero_item.is_deleted:
             if not question_dialog(self, _('Undelete items?'),
                    '<p>'+_('Items must be undeleted to continue. Do you want '
                            'to do this?')+'<br>'):
                 return
     self.table.blockSignals(True)
     for col_zero_item in self.table.selectedItems():
         # undelete any deleted items
         if col_zero_item.is_deleted:
             col_zero_item.set_is_deleted(False)
             self.to_delete.discard(int(col_zero_item.data(Qt.ItemDataRole.UserRole)))
             orig = self.table.item(col_zero_item.row(), 2)
             orig.setData(Qt.ItemDataRole.DisplayRole, '')
     self.table.blockSignals(False)
     self.table.editItem(item)
Exemplo n.º 46
0
 def check_for_existing_isbns(self, books):
     db = self.gui.current_db.new_api
     book_id_identifiers = db.all_field_for('identifiers', db.all_book_ids(tuple))
     existing_isbns = {normalize_isbn(ids.get('isbn', '')): book_id for book_id, ids in book_id_identifiers.items()}
     existing_isbns.pop('', None)
     ok = []
     duplicates = []
     for book in books:
         q = normalize_isbn(book['isbn'])
         if q and q in existing_isbns:
             duplicates.append((book, existing_isbns[q]))
         else:
             ok.append(book)
     if duplicates:
         det_msg = '\n'.join(f'{book["isbn"]}: {db.field_for("title", book_id)}' for book, book_id in duplicates)
         if question_dialog(self.gui, _('Duplicates found'), _(
             'Books with some of the specified ISBNs already exist in the calibre library.'
             ' Click "Show details" for the full list. Do you want to add them anyway?'), det_msg=det_msg
         ):
             ok += [x[0] for x in duplicates]
     return ok
Exemplo n.º 47
0
 def set_email_settings(self, to_set):
     from_ = unicode(self.email_from.text()).strip()
     if to_set and not from_:
         error_dialog(self, _('Bad configuration'),
                      _('You must set the From email address')).exec_()
         return False
     username = unicode(self.relay_username.text()).strip()
     password = unicode(self.relay_password.text()).strip()
     host = unicode(self.relay_host.text()).strip()
     enc_method = ('TLS' if self.relay_tls.isChecked() else
                   'SSL' if self.relay_ssl.isChecked() else 'NONE')
     if host:
         # Validate input
         if ((username and not password) or (not username and password)):
             error_dialog(
                 self, _('Bad configuration'),
                 _('You must either set both the username <b>and</b> password for '
                   'the mail server or no username and no password at all.')
             ).exec_()
             return False
         if not username and not password and enc_method != 'NONE':
             error_dialog(
                 self, _('Bad configuration'),
                 _('Please enter a username and password or set'
                   ' encryption to None ')).exec_()
             return False
         if not (username and password) and not question_dialog(
                 self, _('Are you sure?'),
                 _('No username and password set for mailserver. Most '
                   ' mailservers need a username and password. Are you sure?'
                   )):
             return False
     conf = smtp_prefs()
     conf.set('from_', from_)
     conf.set('relay_host', host if host else None)
     conf.set('relay_port', self.relay_port.value())
     conf.set('relay_username', username if username else None)
     conf.set('relay_password', hexlify(password.encode('utf-8')))
     conf.set('encryption', enc_method)
     return True
Exemplo n.º 48
0
    def add_formats(self, *args):
        if self.gui.stack.currentIndex() != 0:
            return
        view = self.gui.library_view
        rows = view.selectionModel().selectedRows()
        if not rows:
            return error_dialog(self.gui,
                                _('No books selected'),
                                _('Cannot add files as no books are selected'),
                                show=True)
        ids = [view.model().id(r) for r in rows]

        if len(ids) > 1 and not question_dialog(
                self.gui, _('Are you sure'),
                _('Are you sure you want to add the same'
                  ' files to all %d books? If the format'
                  ' already exists for a book, it will be replaced.') %
                len(ids)):
            return

        books = choose_files(self.gui,
                             'add formats dialog dir',
                             _('Select book files'),
                             filters=get_filters())
        if not books:
            return

        db = view.model().db
        for id_ in ids:
            for fpath in books:
                fmt = os.path.splitext(fpath)[1][1:].upper()
                if fmt:
                    db.add_format_with_hooks(id_,
                                             fmt,
                                             fpath,
                                             index_is_id=True,
                                             notify=True)
        current_idx = self.gui.library_view.currentIndex()
        if current_idx.isValid():
            view.model().current_changed(current_idx, current_idx)
Exemplo n.º 49
0
 def validate_import(self):
     from calibre.gui2.ui import get_gui
     g = get_gui()
     if g is not None:
         if g.iactions['Connect Share'].content_server_is_running:
             error_dialog(self, _('Content server running'), _(
                 'Cannot import while the Content server is running, shut it down first by clicking the'
                 ' "Connect/share" button on the calibre toolbar'), show=True)
             return False
     if self.import_panel.stack.currentIndex() == 0:
         error_dialog(self, _('No folder selected'), _(
             'You must select a folder containing the previously exported data that you wish to import'), show=True)
         return False
     else:
         blanks = []
         for w in self.imported_lib_widgets:
             newloc = w.path
             if not newloc:
                 blanks.append(w.lpath)
                 continue
             if iswindows and len(newloc) > LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT:
                 error_dialog(self, _('Too long'),
                     _('Path to library ({0}) too long. Must be less than'
                     ' {1} characters.').format(newloc, LibraryDatabase.WINDOWS_LIBRARY_PATH_LIMIT), show=True)
                 return False
             if not os.path.isdir(newloc):
                 error_dialog(self, _('Not a folder'), _('%s is not a folder')%newloc, show=True)
                 return False
             if os.listdir(newloc):
                 error_dialog(self, _('Folder not empty'), _('%s is not an empty folder')%newloc, show=True)
                 return False
         if blanks:
             if len(blanks) == len(self.imported_lib_widgets):
                 error_dialog(self, _('No libraries selected'), _(
                     'You must specify the location for at least one library'), show=True)
                 return False
             if not question_dialog(self, _('Some libraries ignored'), _(
                     'You have chosen not to import some libraries. Proceed anyway?')):
                 return False
     return True
Exemplo n.º 50
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)
Exemplo n.º 51
0
    def do_tweak(self, book_id):
        if self.gui.current_view() is not self.gui.library_view:
            return error_dialog(self.gui, _('Cannot edit book'), _(
                'Editing of books on the device is not supported'), show=True)
        from calibre.ebooks.oeb.polish.main import SUPPORTED
        db = self.gui.library_view.model().db
        fmts = db.formats(book_id, index_is_id=True) or ''
        fmts = [x.upper().strip() for x in fmts.split(',') if x]
        tweakable_fmts = set(fmts).intersection(SUPPORTED)
        if not tweakable_fmts:
            if not fmts:
                if not question_dialog(self.gui, _('No editable formats'),
                    _('Do you want to create an empty EPUB file to edit?')):
                    return
                tweakable_fmts = {'EPUB'}
                self.gui.iactions['Add Books'].add_empty_format_to_book(book_id, 'EPUB')
                current_idx = self.gui.library_view.currentIndex()
                if current_idx.isValid():
                    self.gui.library_view.model().current_changed(current_idx, current_idx)
            else:
                return error_dialog(self.gui, _('Cannot edit book'), _(
                    'The book must be in the %s formats to edit.'
                    '\n\nFirst convert the book to one of these formats.'
                ) % (_(' or ').join(SUPPORTED)), show=True)
        from calibre.gui2.tweak_book import tprefs
        tprefs.refresh()  # In case they were changed in a Tweak Book process
        if len(tweakable_fmts) > 1:
            if tprefs['choose_tweak_fmt']:
                d = Choose(sorted(tweakable_fmts, key=tprefs.defaults['tweak_fmt_order'].index), self.gui)
                if d.exec() != QDialog.DialogCode.Accepted:
                    return
                tweakable_fmts = {d.fmt}
            else:
                fmts = [f for f in tprefs['tweak_fmt_order'] if f in tweakable_fmts]
                if not fmts:
                    fmts = [f for f in tprefs.defaults['tweak_fmt_order'] if f in tweakable_fmts]
                tweakable_fmts = {fmts[0]}

        fmt = tuple(tweakable_fmts)[0]
        self.ebook_edit_format(book_id, fmt)
Exemplo n.º 52
0
 def _rename_tag(self, item):
     if item is None:
         error_dialog(
             self, _('No item selected'),
             _('You must select one item from the list of Available items.')
         ).exec_()
         return
     col_zero_item = self.table.item(item.row(), 0)
     if col_zero_item.is_deleted:
         if not question_dialog(
                 self, _('Undelete item?'), '<p>' +
                 _('That item is deleted. Do you want to undelete it?') +
                 '<br>'):
             return
         col_zero_item.set_is_deleted(False)
         self.to_delete.discard(int(col_zero_item.data(Qt.UserRole)))
         orig = self.table.item(col_zero_item.row(), 2)
         self.table.blockSignals(True)
         orig.setData(Qt.DisplayRole, '')
         self.table.blockSignals(False)
     else:
         self.table.editItem(item)
Exemplo n.º 53
0
    def do_tag_item_delete(self, category, item_id, orig_name, restrict_to_book_ids=None):
        '''
        Delete an item from some category.
        '''
        if restrict_to_book_ids:
            msg = _('%s will be deleted from books in the virtual library. Are you sure?')%orig_name
        else:
            msg = _('%s will be deleted from all books. Are you sure?')%orig_name
        if not question_dialog(self.tags_view,
                    title=_('Delete item'),
                    msg='<p>'+ msg,
                    skip_dialog_name='tag_item_delete',
                    skip_dialog_msg=_('Show this confirmation again')):
            return
        self.current_db.new_api.remove_items(category, (item_id,), restrict_to_book_ids=restrict_to_book_ids)
        if restrict_to_book_ids is None:
            m = self.tags_view.model()
            m.delete_item_from_all_user_categories(orig_name, category)

        # Clean up the library view
        self.do_tag_item_renamed()
        self.tags_view.recount()
Exemplo n.º 54
0
 def add_plugin(self):
     info = '' if iswindows else ' [.zip %s]'%_('files')
     path = choose_files(self, 'add a plugin dialog', _('Add plugin'),
             filters=[(_('Plugins') + info, ['zip'])], all_files=False,
                 select_only_single_file=True)
     if not path:
         return
     path = path[0]
     if path and os.access(path, os.R_OK) and path.lower().endswith('.zip'):
         if not question_dialog(self, _('Are you sure?'), '<p>' +
                 _('Installing plugins is a <b>security risk</b>. '
                 'Plugins can contain a virus/malware. '
                     'Only install it if you got it from a trusted source.'
                     ' Are you sure you want to proceed?'),
                 show_copy_button=False):
             return
         from calibre.customize.ui import config
         installed_plugins = frozenset(config['plugins'])
         try:
             plugin = add_plugin(path)
         except NameConflict as e:
             return error_dialog(self, _('Already exists'),
                     str(e), show=True)
         self._plugin_model.beginResetModel()
         self._plugin_model.populate()
         self._plugin_model.endResetModel()
         self.changed_signal.emit()
         self.check_for_add_to_toolbars(plugin, previously_installed=plugin.name in installed_plugins)
         info_dialog(self, _('Success'),
                 _('Plugin <b>{0}</b> successfully installed under <b>'
                     '{1} plugins</b>. You may have to restart calibre '
                     'for the plugin to take effect.').format(plugin.name, plugin.type),
                 show=True, show_copy_button=False)
         idx = self._plugin_model.plugin_to_index_by_properties(plugin)
         if idx.isValid():
             self.highlight_index(idx)
     else:
         error_dialog(self, _('No valid plugin path'),
                      _('%s is not a valid plugin path')%path).exec_()
Exemplo n.º 55
0
 def ask_about_inserting_epubs(self):
     '''
     Build question dialog with details about kobo books 
     that couldn't be added to calibre as new books.
     '''
     ''' Terisa: Improve the message
     '''
     caption = PLUGIN_NAME + ' v' + PLUGIN_VERSION
     plural = format_plural(len(self.ids_of_new_books))
     det_msg = ''
     if self.count > 1:
         msg = _('<p><b>{0}</b> EPUB{2} successfully added to library.<br /><br /><b>{1}</b> ').format(len(self.ids_of_new_books), len(self.duplicate_book_list), plural)
         msg += _('not added because books with the same title/author were detected.<br /><br />Would you like to try and add the EPUB format{0}').format(plural)
         msg += _(' to those existing entries?<br /><br />NOTE: no pre-existing EPUBs will be overwritten.')
         for entry in self.duplicate_book_list:
             det_msg += _('{0} -- not added because of {1} in your library.\n\n').format(entry[0].title, entry[2])
     else:
         msg = _('<p><b>{0}</b> -- not added because of {1} in your library.<br /><br />').format(self.duplicate_book_list[0][0].title, self.duplicate_book_list[0][2])
         msg += _('Would you like to try and add the EPUB format to an available calibre duplicate?<br /><br />')
         msg += _('NOTE: no pre-existing EPUB will be overwritten.')
         
     return question_dialog(self.gui, caption, msg, det_msg)
Exemplo n.º 56
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)
 def remove_book(self):
     if not question_dialog(
             self,
             _('Are you sure?'),
             '<p>' + 'Remove the selected book(s) from the series list?',
             show_copy_button=False):
         return
     rows = self.series_table.selectionModel().selectedRows()
     if len(rows) == 0:
         return
     selrows = []
     for row in rows:
         selrows.append(row.row())
     selrows.sort()
     first_sel_row = self.series_table.currentRow()
     for row in reversed(selrows):
         self.books.pop(row)
         self.series_table.removeRow(row)
     if first_sel_row < self.series_table.rowCount():
         self.series_table.select_and_scroll_to_row(first_sel_row)
     elif self.series_table.rowCount() > 0:
         self.series_table.select_and_scroll_to_row(first_sel_row - 1)
     self.renumber_series()
Exemplo n.º 58
0
    def validate(self):
        formats = set(self.format_map())
        extra = formats - set(self.calibre_known_formats)
        if extra:
            fmts = sorted([x.upper() for x in extra])
            if not question_dialog(self, _('Unknown formats'),
                    _('You have enabled the <b>{0}</b> formats for'
                        ' your {1}. The {1} may not support them.'
                        ' If you send these formats to your {1} they '
                        'may not work. Are you sure?').format(
                            (', '.join(fmts)), self.device_name)):
                return False

        tmpl = unicode(self.opt_save_template.text())
        try:
            validation_formatter.validate(tmpl)
            return True
        except Exception as err:
            error_dialog(self, _('Invalid template'),
                    '<p>'+_('The template %s is invalid:')%tmpl +
                    '<br>'+unicode(err), show=True)

            return False
Exemplo n.º 59
0
 def __init__(self, gui, ids, callback):
     from calibre.gui2.dialogs.progress import ProgressDialog
     QObject.__init__(self, gui)
     self.model = gui.library_view.model()
     self.ids = ids
     self.permanent = False
     if can_recycle and len(ids) > 100:
         if question_dialog(gui, _('Are you sure?'), '<p>'+
             _('You are trying to delete %d books. '
                 'Sending so many files to the Recycle'
                 ' Bin <b>can be slow</b>. Should calibre skip the'
                 ' Recycle Bin? If you click Yes the files'
                 ' will be <b>permanently deleted</b>.')%len(ids)):
             self.permanent = True
     self.gui = gui
     self.failures = []
     self.deleted_ids = []
     self.callback = callback
     single_shot(self.delete_one)
     self.pd = ProgressDialog(_('Deleting...'), parent=gui,
             cancelable=False, min=0, max=len(self.ids), icon='trash.png')
     self.pd.setModal(True)
     self.pd.show()
Exemplo n.º 60
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)