Beispiel #1
0
def run_text_search(search, current_editor, current_editor_name, searchable_names, gui_parent, show_editor, edit_file):
    try:
        pat = get_search_regex(search)
    except InvalidRegex as e:
        return error_dialog(gui_parent, _('Invalid regex'), '<p>' + _(
            'The regular expression you entered is invalid: <pre>{0}</pre>With error: {1}').format(
                prepare_string_for_xml(e.regex), error_message(e)), show=True)
    editor, where, files, do_all, marked = initialize_search_request(search, 'count', current_editor, current_editor_name, searchable_names)
    with BusyCursor():
        if editor is not None:
            if editor.find_text(pat):
                return True
            if not files and editor.find_text(pat, wrap=True):
                return True
        for fname, syntax in iteritems(files):
            ed = editors.get(fname, None)
            if ed is not None:
                if ed.find_text(pat, complete=True):
                    show_editor(fname)
                    return True
            else:
                root = current_container().parsed(fname)
                if hasattr(root, 'xpath'):
                    raw = tostring(root, method='text', encoding='unicode', with_tail=True)
                else:
                    raw = current_container().raw_data(fname)
                if pat.search(raw) is not None:
                    edit_file(fname, syntax)
                    if editors[fname].find_text(pat, complete=True):
                        return True

    msg = '<p>' + _('No matches were found for %s') % ('<pre style="font-style:italic">' + prepare_string_for_xml(search['find']) + '</pre>')
    return error_dialog(gui_parent, _('Not found'), msg, show=True)
Beispiel #2
0
    def add_files(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)

        files = choose_files(self.gui, 'tweak-book-bulk-import-files', _('Choose files'))
        if files:
            folder_map = get_recommended_folders(current_container(), files)
            files = {x:('/'.join((folder, os.path.basename(x))) if folder else os.path.basename(x))
                     for x, folder in folder_map.iteritems()}
            self.commit_dirty_opf()
            self.add_savepoint(_('Add files'))
            c = current_container()
            for path, name in files.iteritems():
                i = 0
                while c.exists(name):
                    i += 1
                    name, ext = name.rpartition('.')[0::2]
                    name = '%s_%d.%s' % (name, i, ext)
                try:
                    with open(path, 'rb') as f:
                        c.add_file(name, f.read())
                except:
                    self.rewind_savepoint()
                    raise
            self.gui.file_list.build(c)
            if c.opf_name in editors:
                editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
            self.set_modified()
Beispiel #3
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)
Beispiel #4
0
 def commit_editor_to_container(self, name, container=None):
     container = container or current_container()
     ed = editors[name]
     with container.open(name, 'wb') as f:
         f.write(ed.data)
         if container is current_container():
             ed.is_synced_to_container = True
Beispiel #5
0
 def editor_action(self, action):
     ed = self.gui.central.current_editor
     for n, x in editors.iteritems():
         if x is ed:
             edname = n
             break
     if hasattr(ed, 'action_triggered'):
         if action and action[0] == 'insert_resource':
             rtype = action[1]
             if rtype == 'image' and ed.syntax not in {'css', 'html'}:
                 return error_dialog(self.gui, _('Not supported'), _(
                     'Inserting images is only supported for HTML and CSS files.'), show=True)
             rdata = get_resource_data(rtype, self.gui)
             if rdata is None:
                 return
             if rtype == 'image':
                 chosen_name, chosen_image_is_external = rdata
                 if chosen_image_is_external:
                     with open(chosen_image_is_external[1], 'rb') as f:
                         current_container().add_file(chosen_image_is_external[0], f.read())
                     self.refresh_file_list()
                     chosen_name = chosen_image_is_external[0]
                 href = current_container().name_to_href(chosen_name, edname)
                 ed.insert_image(href)
         else:
             ed.action_triggered(action)
Beispiel #6
0
        def do_all(replace=True):
            count = 0
            if not files and editor is None:
                return 0
            lfiles = files or {name:editor.syntax}

            for n, syntax in lfiles.iteritems():
                if n in editors:
                    raw = editors[n].get_raw_data()
                else:
                    raw = current_container().raw_data(n)
                if replace:
                    raw, num = pat.subn(state['replace'], raw)
                else:
                    num = len(pat.findall(raw))
                count += num
                if replace and num > 0:
                    if n in editors:
                        editors[n].replace_data(raw)
                    else:
                        with current_container().open(n, 'wb') as f:
                            f.write(raw.encode('utf-8'))
            QApplication.restoreOverrideCursor()
            count_message(_('Replaced') if replace else _('Found'), count)
            return count
Beispiel #7
0
 def check_requested(self, *args):
     if current_container() is None:
         return
     self.commit_all_editors_to_container()
     c = self.gui.check_book
     c.parent().show()
     c.parent().raise_()
     c.run_checks(current_container())
Beispiel #8
0
 def reorder_spine(self, items):
     # TODO: If content.opf is dirty in an editor, abort, calling
     # file_list.build(current_container) to undo drag and drop
     self.add_savepoint(_('Re-order text'))
     c = current_container()
     c.set_spine(items)
     self.gui.action_save.setEnabled(True)
     self.gui.file_list.build(current_container())  # needed as the linear flag may have changed on some items
Beispiel #9
0
    def show_context_menu(self, point):
        item = self.itemAt(point)
        if item is None or item in set(self.categories.itervalues()):
            return
        m = QMenu(self)
        sel = self.selectedItems()
        num = len(sel)
        container = current_container()
        ci = self.currentItem()
        if ci is not None:
            cn = unicode(ci.data(0, NAME_ROLE) or '')
            mt = unicode(ci.data(0, MIME_ROLE) or '')
            cat = unicode(ci.data(0, CATEGORY_ROLE) or '')
            n = elided_text(cn.rpartition('/')[-1])
            m.addAction(QIcon(I('save.png')), _('Export %s') % n, partial(self.export, cn))
            if cn not in container.names_that_must_not_be_changed and cn not in container.names_that_must_not_be_removed and mt not in OEB_FONTS:
                m.addAction(_('Replace %s with file...') % n, partial(self.replace, cn))
            if num > 1:
                m.addAction(QIcon(I('save.png')), _('Export all %d selected files') % num, self.export_selected)

            m.addSeparator()

            m.addAction(QIcon(I('modified.png')), _('&Rename %s') % n, self.edit_current_item)
            if is_raster_image(mt):
                m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover image') % n, partial(self.mark_as_cover, cn))
            elif current_container().SUPPORTS_TITLEPAGES and mt in OEB_DOCS and cat == 'text':
                m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover page') % n, partial(self.mark_as_titlepage, cn))
            m.addSeparator()

        if num > 0:
            m.addSeparator()
            if num > 1:
                m.addAction(QIcon(I('modified.png')), _('&Bulk rename the selected files'), self.request_bulk_rename)
            m.addAction(QIcon(I('modified.png')), _('Change the file extension for the selected files'), self.request_change_ext)
            m.addAction(QIcon(I('trash.png')), ngettext(
                '&Delete the selected file', '&Delete the {} selected files', num).format(num), self.request_delete)
            m.addAction(QIcon(I('edit-copy.png')), ngettext(
                '&Copy the selected file to another editor instance',
                '&Copy the {} selected files to another editor instance', num).format(num), self.copy_selected_files)
            m.addSeparator()

        selected_map = defaultdict(list)
        for item in sel:
            selected_map[unicode(item.data(0, CATEGORY_ROLE) or '')].append(unicode(item.data(0, NAME_ROLE) or ''))

        for items in selected_map.itervalues():
            items.sort(key=self.index_of_name)

        if selected_map['text']:
            m.addAction(QIcon(I('format-text-color.png')), _('Link &stylesheets...'), partial(self.link_stylesheets, selected_map['text']))

        if len(selected_map['text']) > 1:
            m.addAction(QIcon(I('merge.png')), _('&Merge selected text files'), partial(self.start_merge, 'text', selected_map['text']))
        if len(selected_map['styles']) > 1:
            m.addAction(QIcon(I('merge.png')), _('&Merge selected style files'), partial(self.start_merge, 'styles', selected_map['styles']))

        if len(list(m.actions())) > 0:
            m.popup(self.mapToGlobal(point))
Beispiel #10
0
 def reorder_spine(self, items):
     self.commit_dirty_opf()
     self.add_savepoint(_('Re-order text'))
     c = current_container()
     c.set_spine(items)
     self.set_modified()
     self.gui.file_list.build(current_container())  # needed as the linear flag may have changed on some items
     if c.opf_name in editors:
         editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
Beispiel #11
0
 def delete_selected(self):
     if self.DELETE_POSSIBLE:
         locations = self.selected_locations
         if locations:
             names = frozenset(locations)
             spine_names = {n for n, l in current_container().spine_names}
             other_items = names - spine_names
             spine_items = [(name, name in names) for name, is_linear in current_container().spine_names]
             self.delete_requested.emit(spine_items, other_items)
Beispiel #12
0
 def edit_current_item(self):
     if not current_container().SUPPORTS_FILENAMES:
         error_dialog(self, _('Cannot rename'), _(
             '%s books do not support file renaming as they do not use file names'
             ' internally. The filenames you see are automatically generated from the'
             ' internal structures of the original file.') % current_container().book_type.upper(), show=True)
         return
     if self.currentItem() is not None:
         self.editItem(self.currentItem())
Beispiel #13
0
 def reorder_spine(self, items):
     if not self.check_opf_dirtied():
         return
     self.add_savepoint(_('Re-order text'))
     c = current_container()
     c.set_spine(items)
     self.gui.action_save.setEnabled(True)
     self.gui.file_list.build(current_container())  # needed as the linear flag may have changed on some items
     if c.opf_name in editors:
         editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
Beispiel #14
0
 def commit_editor_to_container(self, name, container=None):
     container = container or current_container()
     ed = editors[name]
     with container.open(name, 'wb') as f:
         f.write(ed.data)
     if name == container.opf_name:
         container.refresh_mime_map()
     if container is current_container():
         ed.is_synced_to_container = True
         if name == container.opf_name:
             self.gui.file_list.build(container)
Beispiel #15
0
    def mark_requested(self, name, action):
        self.commit_dirty_opf()
        c = current_container()
        if action == 'cover':
            mark_as_cover(current_container(), name)
        elif action.startswith('titlepage:'):
            action, move_to_start = action.partition(':')[0::2]
            move_to_start = move_to_start == 'True'
            mark_as_titlepage(current_container(), name, move_to_start=move_to_start)

        if c.opf_name in editors:
            editors[c.opf_name].replace_data(c.raw_data(c.opf_name))
        self.gui.file_list.build(c)
        self.set_modified()
Beispiel #16
0
 def request_rename_common(self):
     if not current_container().SUPPORTS_FILENAMES:
         error_dialog(self, _('Cannot rename'), _(
             '%s books do not support file renaming as they do not use file names'
             ' internally. The filenames you see are automatically generated from the'
             ' internal structures of the original file.') % current_container().book_type.upper(), show=True)
         return
     names = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()}
     bad = names & current_container().names_that_must_not_be_changed
     if bad:
         error_dialog(self, _('Cannot rename'),
                      _('The file(s) %s cannot be renamed.') % ('<b>%s</b>' % ', '.join(bad)), show=True)
         return
     names = sorted(names, key=self.index_of_name)
     return names
Beispiel #17
0
 def replace(self, name):
     c = current_container()
     mt = c.mime_map[name]
     oext = name.rpartition(".")[-1].lower()
     filters = [oext]
     fname = _("Files")
     if mt in OEB_DOCS:
         fname = _("HTML Files")
         filters = "html htm xhtm xhtml shtml".split()
     elif is_raster_image(mt):
         fname = _("Images")
         filters = "jpeg jpg gif png".split()
     path = choose_files(
         self, "tweak_book_import_file", _("Choose file"), filters=[(fname, filters)], select_only_single_file=True
     )
     if not path:
         return
     path = path[0]
     ext = path.rpartition(".")[-1].lower()
     force_mt = None
     if mt in OEB_DOCS:
         force_mt = c.guess_type("a.html")
     nname = os.path.basename(path)
     nname, ext = nname.rpartition(".")[0::2]
     nname = nname + "." + ext.lower()
     self.replace_requested.emit(name, path, nname, force_mt)
Beispiel #18
0
    def get_completion_data(self, editor, ev=None):
        c = editor.textCursor()
        block, offset = c.block(), c.positionInBlock()
        oblock, boundary = next_tag_boundary(block, offset, forward=False, max_lines=5)
        if boundary is None or not boundary.is_start or boundary.closing:
            # Not inside a opening tag definition
            return
        tagname = boundary.name.lower()
        startpos = oblock.position() + boundary.offset
        c.setPosition(c.position()), c.setPosition(startpos, c.KeepAnchor)
        text = c.selectedText()
        m = self.complete_attr_pat.search(text)
        if m is None:
            return
        attr = m.group(1).lower().split(':')[-1]
        doc_name = editor.completion_doc_name
        if doc_name and attr in {'href', 'src'}:
            # A link
            query = m.group(2) or m.group(3) or ''
            c = current_container()
            names_type = {'a':'text_link', 'img':'image', 'image':'image', 'link':'stylesheet'}.get(tagname)
            idx = query.find('#')
            if idx > -1 and names_type in (None, 'text_link'):
                href, query = query[:idx], query[idx+1:]
                name = c.href_to_name(href) if href else doc_name
                if c.mime_map.get(name) in OEB_DOCS:
                    return 'complete_anchor', name, query

            return 'complete_names', (names_type, doc_name, c.root), query
Beispiel #19
0
    def fix_html(self):
        if self.syntax == "html":
            from calibre.ebooks.oeb.polish.pretty import fix_html

            self.editor.replace_text(fix_html(current_container(), unicode(self.editor.toPlainText())).decode("utf-8"))
            return True
        return False
Beispiel #20
0
def set_data(name, val):
    if name in editors:
        editors[name].replace_data(val, only_if_different=False)
    else:
        with current_container().open(name, 'wb') as f:
            f.write(val)
    get_boss().set_modified()
Beispiel #21
0
    def __init__(self, title=None, parent=None):
        QDialog.__init__(self, parent)

        t = title or current_container().mi.title
        self.book_title = t
        self.setWindowTitle(_('Edit the ToC in %s')%t)
        self.setWindowIcon(QIcon(I('toc.png')))

        l = self.l = QVBoxLayout()
        self.setLayout(l)

        self.stacks = s = QStackedWidget(self)
        l.addWidget(s)
        self.toc_view = TOCView(self)
        self.toc_view.add_new_item.connect(self.add_new_item)
        s.addWidget(self.toc_view)
        self.item_edit = ItemEdit(self)
        s.addWidget(self.item_edit)

        bb = self.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
        l.addWidget(bb)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)

        self.read_toc()

        self.resize(950, 630)
        geom = gprefs.get('toc_editor_window_geom', None)
        if geom is not None:
            self.restoreGeometry(bytes(geom))
Beispiel #22
0
 def edit_file(self, name, syntax):
     editor = editors.get(name, None)
     if editor is None:
         editor = editors[name] = editor_from_syntax(syntax, self.gui.editor_tabs)
         data = current_container().raw_data(name)
         self.init_editor(name, editor, data)
     self.show_editor(name)
Beispiel #23
0
 def update_editors_from_container(self, container=None):
     c = container or current_container()
     for name, ed in tuple(editors.iteritems()):
         if c.has_name(name):
             ed.replace_data(c.raw_data(name))
         else:
             self.close_editor(name)
Beispiel #24
0
 def pretty_print(self, name):
     from calibre.ebooks.oeb.polish.pretty import pretty_html, pretty_css, pretty_xml
     if self.syntax in {'css', 'html', 'xml'}:
         func = {'css':pretty_css, 'xml':pretty_xml}.get(self.syntax, pretty_html)
         self.editor.replace_text(func(current_container(), name, unicode(self.editor.toPlainText())).decode('utf-8'))
         return True
     return False
Beispiel #25
0
 def show(self, name):
     if name != self.current_name:
         self.refresh_timer.stop()
         self.current_name = name
         parse_worker.add_request(name)
         self.view.setUrl(QUrl.fromLocalFile(current_container().name_to_abspath(name)))
         return True
Beispiel #26
0
 def add_file(self):
     if not self.check_opf_dirtied():
         return
     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(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)
Beispiel #27
0
 def link_stylesheets_requested(self, names, sheets):
     self.commit_all_editors_to_container()
     self.add_savepoint(_('Link stylesheets'))
     changed_names = link_stylesheets(current_container(), names, sheets)
     if changed_names:
         self.update_editors_from_container(names=changed_names)
         self.set_modified()
Beispiel #28
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
Beispiel #29
0
    def process_rule(self, rule, is_ancestor, maximum_specificities):
        selector = rule['selector']
        sheet_index = rule['sheet_index']
        rule_address = rule['rule_address'] or ()
        if selector is not None:
            try:
                specificity = [0] + list(parse(selector)[0].specificity())
            except (AttributeError, TypeError, SelectorError):
                specificity = [0, 0, 0, 0]
        else:  # style attribute
            specificity = [1, 0, 0, 0]
        specificity.extend((sheet_index, tuple(rule_address)))
        ancestor_specificity = 0 if is_ancestor else 1
        properties = []
        for prop in rule['properties']:
            important = 1 if prop[-1] == 'important' else 0
            p = Property(prop, [ancestor_specificity] + [important] + specificity)
            properties.append(p)
            if p.specificity > maximum_specificities.get(p.name, (0,0,0,0,0,0)):
                maximum_specificities[p.name] = p.specificity
        rule['properties'] = properties

        href = rule['href']
        if hasattr(href, 'startswith') and href.startswith('file://'):
            href = href[len('file://'):]
            if iswindows and href.startswith('/'):
                href = href[1:]
            if href:
                rule['href'] = current_container().abspath_to_name(href, root=self.preview.current_root)
Beispiel #30
0
    def setup_ui(self):
        self.l = l = QVBoxLayout(self)
        self.setLayout(l)

        self.msg = m = QLabel(self.msg or _(
        'Choose the folder into which the files will be placed'))
        l.addWidget(m)
        m.setWordWrap(True)

        self.folders = f = QTreeWidget(self)
        f.setHeaderHidden(True)
        f.itemDoubleClicked.connect(self.accept)
        l.addWidget(f)
        f.setContextMenuPolicy(Qt.CustomContextMenu)
        f.customContextMenuRequested.connect(self.show_context_menu)
        self.root = QTreeWidgetItem(f, ('/',))

        def process(node, parent):
            parent.setIcon(0, QIcon(I('mimetypes/dir.png')))
            for child in sorted(node, key=numeric_sort_key):
                c = QTreeWidgetItem(parent, (child,))
                process(node[child], c)
        process(create_folder_tree(current_container()), self.root)
        self.root.setSelected(True)
        f.expandAll()

        l.addWidget(self.bb)
Beispiel #31
0
 def createRequest(self, operation, request, data):
     url = unicode(request.url().toString())
     if operation == self.GetOperation and url.startswith('file://'):
         path = url[7:]
         if iswindows and path.startswith('/'):
             path = path[1:]
         c = current_container()
         name = c.abspath_to_name(path)
         if c.has_name(name):
             try:
                 return NetworkReply(
                     self, request,
                     c.mime_map.get(name, 'application/octet-stream'), name)
             except Exception:
                 import traceback
                 traceback.print_exc()
     return QNetworkAccessManager.createRequest(self, operation, request,
                                                data)
Beispiel #32
0
 def import_image(self):
     ans = choose_images(self,
                         'add-cover-choose-image',
                         _('Choose a cover image'),
                         formats=('jpg', 'jpeg', 'png', 'gif'))
     if ans:
         from calibre.gui2.tweak_book.file_list import NewFileDialog
         d = NewFileDialog(self)
         d.do_import_file(ans[0], hide_button=True)
         if d.exec_() == d.Accepted:
             self.import_requested.emit(d.file_name, d.file_data)
             self.container = current_container()
             self.names_filter.clear()
             self.names.model().set_names(
                 sorted(self.image_names, key=sort_key))
             i = self.names.model().find_name(d.file_name)
             self.names.setCurrentIndex(self.names.model().index(i))
             self.current_image_changed()
Beispiel #33
0
 def name_is_ok(self):
     name = unicode(self.name.text())
     if not name or not name.strip():
         return self.show_error('')
     ext = name.rpartition('.')[-1]
     if not ext or ext == name:
         return self.show_error(_('The file name must have an extension'))
     norm = name.replace('\\', '/')
     parts = name.split('/')
     for x in parts:
         if sanitize_file_name_unicode(x) != x:
             return self.show_error(
                 _('The file name contains invalid characters'))
     if current_container().has_name(norm):
         return self.show_error(
             _('This file name already exists in the book'))
     self.show_error('')
     return True
Beispiel #34
0
 def change_fonts(self):
     fonts = self.get_selected_data()
     if not fonts:
         return
     d = ChangeFontFamily(
         ', '.join(fonts),
         {f
          for f, embedded in iteritems(self.model.font_data) if embedded},
         self)
     if d.exec_() != d.Accepted:
         return
     changed = False
     new_family = d.normalized_family
     for font in fonts:
         changed |= change_font(current_container(), font, new_family)
     if changed:
         self.model.build()
         self.container_changed.emit()
Beispiel #35
0
    def build(self):
        c = current_container()
        if c is None:
            return
        toc = get_toc(c, verify_destinations=False)

        def process_node(toc, parent):
            for child in toc:
                node = QTreeWidgetItem(parent)
                node.setText(0, child.title or '')
                node.setData(0, DEST_ROLE, child.dest or '')
                node.setData(0, FRAG_ROLE, child.frag or '')
                tt = _('File: {0}\nAnchor: {1}').format(
                    child.dest or '', child.frag or _('Top of file'))
                node.setData(0, Qt.ToolTipRole, tt)
                process_node(child, node)

        self.view.clear()
        process_node(toc, self.view.invisibleRootItem())
Beispiel #36
0
 def sort_css(self):
     from calibre.gui2.dialogs.confirm_delete import confirm
     if confirm(_(
             'Sorting CSS rules can in rare cases change the effective styles applied to the book.'
             ' Are you sure you want to proceed?'),
                'edit-book-confirm-sort-css',
                parent=self,
                config_set=tprefs):
         c = self.textCursor()
         c.beginEditBlock()
         c.movePosition(c.Start), c.movePosition(c.End, c.KeepAnchor)
         text = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR,
                                                  '\n').rstrip('\0')
         from calibre.ebooks.oeb.polish.css import sort_sheet
         text = sort_sheet(current_container(), text).cssText
         c.insertText(text)
         c.movePosition(c.Start)
         c.endEditBlock()
         self.setTextCursor(c)
Beispiel #37
0
    def get_droppable_files(self, md):

        def is_mt_ok(mt):
            return self.syntax == 'html' and (
                mt in OEB_DOCS or mt in OEB_STYLES or mt.startswith('image/')
            )

        if md.hasFormat(CONTAINER_DND_MIMETYPE):
            for line in as_unicode(bytes(md.data(CONTAINER_DND_MIMETYPE))).splitlines():
                mt = current_container().mime_map.get(line, 'application/octet-stream')
                if is_mt_ok(mt):
                    yield line, mt, True
            return
        for qurl in md.urls():
            if qurl.isLocalFile() and os.access(qurl.toLocalFile(), os.R_OK):
                path = qurl.toLocalFile()
                mt = guess_type(path)
                if is_mt_ok(mt):
                    yield path, mt, False
Beispiel #38
0
    def request_delete(self):
        names = self.selected_names
        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 range(text.childCount()))
        spine_removals = [(str(item.data(0, NAME_ROLE)
                               or ''), item.isSelected()) for item in children]
        other_removals = {
            str(item.data(0, NAME_ROLE) or '')
            for item in self.selectedItems()
            if str(item.data(0, CATEGORY_ROLE) or '') != 'text'
        }
        self.delete_requested.emit(spine_removals, other_removals)
Beispiel #39
0
 def do_find():
     if editor is not None:
         if editor.find(pat, marked=marked):
             return
         if not files:
             if not state['wrap']:
                 return no_match()
             return editor.find(pat, wrap=True,
                                marked=marked) or no_match()
     for fname, syntax in files.iteritems():
         if fname in editors:
             if not editors[fname].find(pat, complete=True):
                 continue
             return self.show_editor(fname)
         raw = current_container().raw_data(fname)
         if pat.search(raw) is not None:
             self.edit_file(fname, syntax)
             if editors[fname].find(pat, complete=True):
                 return
     return no_match()
Beispiel #40
0
    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, empty_index)  # draw the hover and selection highlights
        name = unicode_type(index.data(Qt.DisplayRole) or '')
        cover = self.cover_cache.get(name, None)
        if cover is None:
            cover = self.cover_cache[name] = QPixmap()
            try:
                raw = current_container().raw_data(name, decode=False)
            except:
                pass
            else:
                try:
                    dpr = painter.device().devicePixelRatioF()
                except AttributeError:
                    dpr = painter.device().devicePixelRatio()
                cover.loadFromData(raw)
                cover.setDevicePixelRatio(dpr)
                if not cover.isNull():
                    scaled, width, height = fit_image(cover.width(), cover.height(), self.cover_size.width(), self.cover_size.height())
                    if scaled:
                        cover = self.cover_cache[name] = cover.scaled(int(dpr*width), int(dpr*height), transformMode=Qt.SmoothTransformation)

        painter.save()
        try:
            rect = option.rect
            rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN)
            trect = QRect(rect)
            rect.setBottom(rect.bottom() - self.title_height)
            if not cover.isNull():
                dx = max(0, int((rect.width() - int(cover.width()/cover.devicePixelRatio()))/2.0))
                dy = max(0, rect.height() - int(cover.height()/cover.devicePixelRatio()))
                rect.adjust(dx, dy, -dx, 0)
                painter.drawPixmap(rect, cover)
            rect = trect
            rect.setTop(rect.bottom() - self.title_height + 5)
            painter.setRenderHint(QPainter.TextAntialiasing, True)
            metrics = painter.fontMetrics()
            painter.drawText(rect, Qt.AlignCenter|Qt.TextSingleLine,
                                metrics.elidedText(name, Qt.ElideLeft, rect.width()))
        finally:
            painter.restore()
Beispiel #41
0
    def setup_ui(self):
        from calibre.ebooks.oeb.polish.images import get_compressible_images
        self.setWindowIcon(QIcon(I('compress-image.png')))
        self.h = h = QHBoxLayout(self)
        self.images = i = QListWidget(self)
        h.addWidget(i)
        self.l = l = QVBoxLayout()
        h.addLayout(l)
        c = current_container()
        for name in sorted(get_compressible_images(c), key=numeric_sort_key):
            x = QListWidgetItem(name, i)
            x.setData(Qt.ItemDataRole.UserRole, c.filesize(name))
        i.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
        i.setMinimumHeight(350), i.setMinimumWidth(350)
        i.selectAll(), i.setSpacing(5)
        self.delegate = ImageItemDelegate(self)
        i.setItemDelegate(self.delegate)
        self.la = la = QLabel(_(
            'You can compress the images in this book losslessly, reducing the file size of the book,'
            ' without affecting image quality. Typically image size is reduced by 5 - 15%.'))
        la.setWordWrap(True)
        la.setMinimumWidth(250)
        l.addWidget(la), l.addSpacing(30)

        self.enable_lossy = el = QCheckBox(_('Enable &lossy compression of JPEG images'))
        el.setToolTip(_('This allows you to change the quality factor used for JPEG images.\nBy lowering'
                        ' the quality you can greatly reduce file size, at the expense of the image looking blurred.'))
        l.addWidget(el)
        self.h2 = h = QHBoxLayout()
        l.addLayout(h)
        self.jq = jq = QSpinBox(self)
        jq.setMinimum(0), jq.setMaximum(100), jq.setValue(tprefs.get('jpeg_compression_quality_for_lossless_compression', 80)), jq.setEnabled(False)
        jq.setToolTip(_('The compression quality, 1 is high compression, 100 is low compression.\nImage'
                        ' quality is inversely correlated with compression quality.'))
        jq.valueChanged.connect(self.save_compression_quality)
        el.toggled.connect(jq.setEnabled)
        self.jql = la = QLabel(_('Compression &quality:'))
        la.setBuddy(jq)
        h.addWidget(la), h.addWidget(jq)
        l.addStretch(10)
        l.addWidget(self.bb)
Beispiel #42
0
 def current_changed(self, current, previous):
     link = current.data(Qt.UserRole)
     if link is None:
         return
     url = None
     if link.is_external:
         if link.href:
             frag = ('#' + link.anchor.id) if link.anchor.id else ''
             url = QUrl(link.href + frag)
     elif link.anchor.location:
         path = current_container().name_to_abspath(link.anchor.location.name)
         if path and os.path.exists(path):
             url = QUrl.fromLocalFile(path)
             if link.anchor.id:
                 url.setFragment(link.anchor.id)
     if url is None:
         self.view.setHtml('<p>' + _('No destination found for this link'))
         self.current_url = url
     elif url != self.current_url:
         self.current_url = url
         self.view.setUrl(url)
Beispiel #43
0
 def createRequest(self, operation, request, data):
     url = unicode(request.url().toString(QUrl.None))
     if operation == self.GetOperation and url.startswith('file://'):
         path = url[7:]
         if iswindows and path.startswith('/'):
             path = path[1:]
         c = current_container()
         try:
             name = c.abspath_to_name(path, root=self.current_root)
         except ValueError:  # Happens on windows with absolute paths on different drives
             name = None
         if c.has_name(name):
             try:
                 return NetworkReply(
                     self, request,
                     c.mime_map.get(name, 'application/octet-stream'), name)
             except Exception:
                 import traceback
                 traceback.print_exc()
     return QNetworkAccessManager.createRequest(self, operation, request,
                                                data)
Beispiel #44
0
 def do_find():
     for p, __ in searches:
         if editor is not None:
             if editor.find(p, marked=marked, save_match='gui'):
                 return True
             if wrap and not files and editor.find(p, wrap=True, marked=marked, save_match='gui'):
                 return True
         for fname, syntax in files.iteritems():
             ed = editors.get(fname, None)
             if ed is not None:
                 if not wrap and ed is editor:
                     continue
                 if ed.find(p, complete=True, save_match='gui'):
                     show_editor(fname)
                     return True
             else:
                 raw = current_container().raw_data(fname)
                 if p.search(raw) is not None:
                     edit_file(fname, syntax)
                     if editors[fname].find(p, complete=True, save_match='gui'):
                         return True
     return no_match()
Beispiel #45
0
    def request_bulk_rename(self):
        names = self.request_rename_common()
        if names is not None:
            categories = Counter(str(item.data(0, CATEGORY_ROLE) or '') for item in self.selectedItems())
            settings = get_bulk_rename_settings(self, len(names), category=categories.most_common(1)[0][0], allow_spine_order=True)
            fmt, num = settings['prefix'], settings['start']
            if fmt is not None:
                def change_name(name, num):
                    parts = name.split('/')
                    base, ext = parts[-1].rpartition('.')[0::2]
                    parts[-1] = (fmt % num) + '.' + ext
                    return '/'.join(parts)
                if settings['spine_order']:
                    order_map = get_spine_order_for_all_files(current_container())
                    select_map = {n:i for i, n in enumerate(names)}

                    def key(n):
                        return order_map.get(n, (sys.maxsize, select_map[n]))
                    name_map = {n: change_name(n, num + i) for i, n in enumerate(sorted(names, key=key))}
                else:
                    name_map = {n:change_name(n, num + i) for i, n in enumerate(names)}
                self.bulk_rename_requested.emit(name_map)
Beispiel #46
0
 def paint(self, painter, option, index):
     top_level = not index.parent().isValid()
     hover = option.state & QStyle.State_MouseOver
     if hover:
         if top_level:
             suffix = '%s(%d)' % (NBSP, index.model().rowCount(index))
         else:
             try:
                 suffix = NBSP + human_readable(current_container().filesize(unicode(index.data(NAME_ROLE) or '')))
             except EnvironmentError:
                 suffix = NBSP + human_readable(0)
         br = painter.boundingRect(option.rect, Qt.AlignRight|Qt.AlignVCenter, suffix)
     if top_level and index.row() > 0:
         option.rect.adjust(0, 5, 0, 0)
         painter.drawLine(option.rect.topLeft(), option.rect.topRight())
         option.rect.adjust(0, 1, 0, 0)
     if hover:
         option.rect.adjust(0, 0, -br.width(), 0)
     QStyledItemDelegate.paint(self, painter, option, index)
     if hover:
         option.rect.adjust(0, 0, br.width(), 0)
         painter.drawText(option.rect, Qt.AlignRight|Qt.AlignVCenter, suffix)
Beispiel #47
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 = str(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)
Beispiel #48
0
    def get_completion_data(self, editor, ev=None):
        c = editor.textCursor()
        block, offset = c.block(), c.positionInBlock()
        oblock, boundary = next_tag_boundary(block,
                                             offset,
                                             forward=False,
                                             max_lines=5)
        if boundary is None or not boundary.is_start or boundary.closing:
            # Not inside a opening tag definition
            return
        tagname = boundary.name.lower()
        startpos = oblock.position() + boundary.offset
        c.setPosition(c.position()), c.setPosition(
            startpos, QTextCursor.MoveMode.KeepAnchor)
        text = c.selectedText()
        m = self.complete_attr_pat.search(text)
        if m is None:
            return
        attr = m.group(1).lower().split(':')[-1]
        doc_name = editor.completion_doc_name
        if doc_name and attr in {'href', 'src'}:
            # A link
            query = m.group(2) or m.group(3) or ''
            c = current_container()
            names_type = {
                'a': 'text_link',
                'img': 'image',
                'image': 'image',
                'link': 'stylesheet'
            }.get(tagname)
            idx = query.find('#')
            if idx > -1 and names_type in (None, 'text_link'):
                href, query = query[:idx], query[idx + 1:]
                name = c.href_to_name(href) if href else doc_name
                if c.mime_map.get(name) in OEB_DOCS:
                    return 'complete_anchor', name, query

            return 'complete_names', (names_type, doc_name, c.root), query
Beispiel #49
0
    def __init__(self, title=None, parent=None):
        QDialog.__init__(self, parent)
        self.last_reject_at = self.last_accept_at = -1000

        t = title or current_container().mi.title
        self.book_title = t
        self.setWindowTitle(_('Edit the ToC in %s') % t)
        self.setWindowIcon(QIcon(I('toc.png')))

        l = self.l = QVBoxLayout()
        self.setLayout(l)

        self.stacks = s = QStackedWidget(self)
        l.addWidget(s)
        self.toc_view = TOCView(self, tprefs)
        self.toc_view.add_new_item.connect(self.add_new_item)
        s.addWidget(self.toc_view)
        self.item_edit = ItemEdit(self, tprefs)
        s.addWidget(self.item_edit)

        bb = self.bb = QDialogButtonBox(
            QDialogButtonBox.StandardButton.Ok
            | QDialogButtonBox.StandardButton.Cancel)
        l.addWidget(bb)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.undo_button = b = bb.addButton(
            _('&Undo'), QDialogButtonBox.ButtonRole.ActionRole)
        b.setToolTip(_('Undo the last action, if any'))
        b.setIcon(QIcon(I('edit-undo.png')))
        b.clicked.connect(self.toc_view.undo)

        self.read_toc()

        self.resize(950, 630)
        geom = tprefs.get('toc_editor_window_geom', None)
        if geom is not None:
            QApplication.instance().safe_restore_geometry(self, bytes(geom))
Beispiel #50
0
 def paint(self, painter, option, index):
     name = index.data(Qt.ItemDataRole.DisplayRole)
     sz = human_readable(index.data(Qt.ItemDataRole.UserRole))
     pmap = index.data(Qt.ItemDataRole.UserRole+1)
     irect = option.rect.adjusted(0, 5, 0, -5)
     irect.setRight(irect.left() + 70)
     if pmap is None:
         pmap = QPixmap(current_container().get_file_path_for_processing(name))
         scaled, nwidth, nheight = fit_image(pmap.width(), pmap.height(), irect.width(), irect.height())
         if scaled:
             pmap = pmap.scaled(nwidth, nheight, transformMode=Qt.TransformationMode.SmoothTransformation)
         index.model().setData(index, pmap, Qt.ItemDataRole.UserRole+1)
     x, y = (irect.width() - pmap.width())//2, (irect.height() - pmap.height())//2
     r = irect.adjusted(x, y, -x, -y)
     QStyledItemDelegate.paint(self, painter, option, empty_index)
     painter.drawPixmap(r, pmap)
     trect = irect.adjusted(irect.width() + 10, 0, 0, 0)
     trect.setRight(option.rect.right())
     painter.save()
     if option.state & QStyle.StateFlag.State_Selected:
         painter.setPen(QPen(option.palette.color(QPalette.ColorRole.HighlightedText)))
     painter.drawText(trect, Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft, name + '\n' + sz)
     painter.restore()
Beispiel #51
0
 def requestStarted(self, rq):
     if bytes(rq.requestMethod()) != b'GET':
         rq.fail(rq.RequestDenied)
         return
     url = rq.requestUrl()
     if url.host() != FAKE_HOST or url.scheme() != FAKE_PROTOCOL:
         rq.fail(rq.UrlNotFound)
         return
     name = url.path()[1:]
     try:
         if name.startswith('calibre_internal-mathjax/'):
             handle_mathjax_request(rq, name.partition('-')[-1])
             return
         c = current_container()
         if not c.has_name(name):
             rq.fail(rq.UrlNotFound)
             return
         mime_type = c.mime_map.get(name, 'application/octet-stream')
         if mime_type in OEB_DOCS:
             mime_type = XHTML_MIME
             self.requests[name].append((mime_type, rq))
             QTimer.singleShot(0, self.check_for_parse)
         else:
             data = get_data(name)
             if isinstance(data, unicode_type):
                 data = data.encode('utf-8')
             mime_type = {
                 # Prevent warning in console about mimetype of fonts
                 'application/vnd.ms-opentype': 'application/x-font-ttf',
                 'application/x-font-truetype': 'application/x-font-ttf',
                 'application/font-sfnt': 'application/x-font-ttf',
             }.get(mime_type, mime_type)
             send_reply(rq, mime_type, data)
     except Exception:
         import traceback
         traceback.print_exc()
         rq.fail(rq.RequestFailed)
Beispiel #52
0
    def save_copy(self):
        c = current_container()
        ext = c.path_to_ebook.rpartition('.')[-1]
        path = choose_save_file(self.gui,
                                'tweak_book_save_copy',
                                _('Choose path'),
                                filters=[(_('Book (%s)') % ext.upper(),
                                          [ext.lower()])],
                                all_files=False)
        if not path:
            return
        tdir = self.mkdtemp(prefix='save-copy-')
        container = clone_container(c, tdir)
        for name, ed in editors.iteritems():
            if ed.is_modified or not ed.is_synced_to_container:
                self.commit_editor_to_container(name, container)

        def do_save(c, path, tdir):
            save_container(c, path)
            shutil.rmtree(tdir, ignore_errors=True)
            return path

        self.gui.blocking_job('save_copy', _('Saving copy, please wait...'),
                              self.copy_saved, do_save, container, path, tdir)
Beispiel #53
0
 def contextMenuEvent(self, ev):
     menu = QMenu(self)
     data = self._page.contextMenuData()
     url = data.linkUrl()
     url = unicode_type(url.toString(NO_URL_FORMATTING)).strip()
     text = data.selectedText()
     if text:
         ca = self.pageAction(QWebEnginePage.WebAction.Copy)
         if ca.isEnabled():
             menu.addAction(ca)
     menu.addAction(actions['reload-preview'])
     menu.addAction(QIcon(I('debug.png')), _('Inspect element'),
                    self.inspect)
     if url.partition(':')[0].lower() in {'http', 'https'}:
         menu.addAction(_('Open link'),
                        partial(safe_open_url, data.linkUrl()))
     if QWebEngineContextMenuData.MediaType.MediaTypeImage <= data.mediaType(
     ) <= QWebEngineContextMenuData.MediaType.MediaTypeFile:
         url = data.mediaUrl()
         if url.scheme() == FAKE_PROTOCOL:
             href = url.path().lstrip('/')
             if href:
                 c = current_container()
                 resource_name = c.href_to_name(href)
                 if resource_name and c.exists(
                         resource_name
                 ) and resource_name not in c.names_that_must_not_be_changed:
                     self.add_open_with_actions(menu, resource_name)
                     if data.mediaType(
                     ) == QWebEngineContextMenuData.MediaType.MediaTypeImage:
                         mime = c.mime_map[resource_name]
                         if mime.startswith('image/'):
                             menu.addAction(
                                 _('Edit %s') % resource_name,
                                 partial(self.edit_image, resource_name))
     menu.exec_(ev.globalPos())
Beispiel #54
0
 def replace(self, name):
     c = current_container()
     mt = c.mime_map[name]
     oext = name.rpartition('.')[-1].lower()
     filters = [oext]
     fname = _('Files')
     if mt in OEB_DOCS:
         fname = _('HTML Files')
         filters = 'html htm xhtm xhtml shtml'.split()
     elif is_raster_image(mt):
         fname = _('Images')
         filters = 'jpeg jpg gif png'.split()
     path = choose_files(self, 'tweak_book_import_file', _('Choose file'), filters=[(fname, filters)], select_only_single_file=True)
     if not path:
         return
     path = path[0]
     ext = path.rpartition('.')[-1].lower()
     force_mt = None
     if mt in OEB_DOCS:
         force_mt = c.guess_type('a.html')
     nname = os.path.basename(path)
     nname, ext = nname.rpartition('.')[0::2]
     nname = nname + '.' + ext.lower()
     self.replace_requested.emit(name, path, nname, force_mt)
Beispiel #55
0
 def read_toc(self):
     self.toc_view(current_container())
     self.item_edit.load(current_container())
     self.stacks.setCurrentIndex(0)
Beispiel #56
0
 def update_window_title(self):
     fname = os.path.basename(current_container().path_to_ebook)
     self.setWindowTitle(
         self.current_metadata.title + ' [%s] :: %s :: %s' %
         (current_container().book_type.upper(), fname, self.APP_NAME))
Beispiel #57
0
 def write_toc(self):
     toc = self.toc_view.create_toc()
     commit_toc(current_container(),
                toc,
                lang=self.toc_view.toc_lang,
                uid=self.toc_view.toc_uid)
Beispiel #58
0
    def show_context_menu(self, pos):
        m = QMenu(self)
        a = m.addAction
        c = self.editor.cursorForPosition(pos)
        origc = QTextCursor(c)
        current_cursor = self.editor.textCursor()
        r = origr = self.editor.syntax_range_for_cursor(c)
        if (
                r is None or not r.format.property(SPELL_PROPERTY)
        ) and c.positionInBlock() > 0 and not current_cursor.hasSelection():
            c.setPosition(c.position() - 1)
            r = self.editor.syntax_range_for_cursor(c)

        if r is not None and r.format.property(SPELL_PROPERTY):
            word = self.editor.text_for_range(c.block(), r)
            locale = self.editor.spellcheck_locale_for_cursor(c)
            orig_pos = c.position()
            c.setPosition(orig_pos - utf16_length(word))
            found = False
            self.editor.setTextCursor(c)
            if self.editor.find_spell_word([word],
                                           locale.langcode,
                                           center_on_cursor=False):
                found = True
                fc = self.editor.textCursor()
                if fc.position() < c.position():
                    self.editor.find_spell_word([word],
                                                locale.langcode,
                                                center_on_cursor=False)
            spell_cursor = self.editor.textCursor()
            if current_cursor.hasSelection():
                # Restore the current cursor so that any selection is preserved
                # for the change case actions
                self.editor.setTextCursor(current_cursor)
            if found:
                suggestions = dictionaries.suggestions(word, locale)[:7]
                if suggestions:
                    for suggestion in suggestions:
                        ac = m.addAction(
                            suggestion,
                            partial(self.editor.simple_replace,
                                    suggestion,
                                    cursor=spell_cursor))
                        f = ac.font()
                        f.setBold(True), ac.setFont(f)
                    m.addSeparator()
                m.addAction(actions['spell-next'])
                m.addAction(_('Ignore this word'),
                            partial(self._nuke_word, None, word, locale))
                dics = dictionaries.active_user_dictionaries
                if len(dics) > 0:
                    if len(dics) == 1:
                        m.addAction(
                            _('Add this word to the dictionary: {0}').format(
                                dics[0].name),
                            partial(self._nuke_word, dics[0].name, word,
                                    locale))
                    else:
                        ac = m.addAction(_('Add this word to the dictionary'))
                        dmenu = QMenu(m)
                        ac.setMenu(dmenu)
                        for dic in dics:
                            dmenu.addAction(
                                dic.name,
                                partial(self._nuke_word, dic.name, word,
                                        locale))
                m.addSeparator()

        if origr is not None and origr.format.property(LINK_PROPERTY):
            href = self.editor.text_for_range(origc.block(), origr)
            m.addAction(
                _('Open %s') % href, partial(self.link_clicked.emit, href))

        if origr is not None and (origr.format.property(TAG_NAME_PROPERTY)
                                  or origr.format.property(CSS_PROPERTY)):
            word = self.editor.text_for_range(origc.block(), origr)
            item_type = 'tag_name' if origr.format.property(
                TAG_NAME_PROPERTY) else 'css_property'
            url = help_url(word,
                           item_type,
                           self.editor.highlighter.doc_name,
                           extra_data=current_container().opf_version)
            if url is not None:
                m.addAction(
                    _('Show help for: %s') % word, partial(open_url, url))

        for x in ('undo', 'redo'):
            ac = actions['editor-%s' % x]
            if ac.isEnabled():
                a(ac)
        m.addSeparator()
        for x in ('cut', 'copy', 'paste'):
            ac = actions['editor-' + x]
            if ac.isEnabled():
                a(ac)
        m.addSeparator()
        m.addAction(_('&Select all'), self.editor.select_all)
        if self.selected_text or self.has_marked_text:
            update_mark_text_action(self)
            m.addAction(actions['mark-selected-text'])
        if self.syntax != 'css' and actions['editor-cut'].isEnabled():
            cm = QMenu(_('Change &case'), m)
            for ac in 'upper lower swap title capitalize'.split():
                cm.addAction(actions['transform-case-' + ac])
            m.addMenu(cm)
        if self.syntax == 'html':
            m.addAction(actions['multisplit'])
        m.exec_(self.editor.viewport().mapToGlobal(pos))
Beispiel #59
0
    def show_context_menu(self, point):
        item = self.itemAt(point)
        if item is None or item in set(self.categories.itervalues()):
            return
        m = QMenu(self)
        sel = self.selectedItems()
        num = len(sel)
        container = current_container()
        ci = self.currentItem()
        if ci is not None:
            cn = unicode(ci.data(0, NAME_ROLE).toString())
            mt = unicode(ci.data(0, MIME_ROLE).toString())
            cat = unicode(ci.data(0, CATEGORY_ROLE).toString())
            n = elided_text(cn.rpartition('/')[-1])
            m.addAction(QIcon(I('save.png')),
                        _('Export %s') % n, partial(self.export, cn))
            if cn not in container.names_that_must_not_be_changed and cn not in container.names_that_must_not_be_removed and mt not in OEB_FONTS:
                m.addAction(
                    _('Replace %s with file...') % n,
                    partial(self.replace, cn))
            m.addSeparator()

            m.addAction(QIcon(I('modified.png')),
                        _('&Rename %s') % n, self.edit_current_item)
            if is_raster_image(mt):
                m.addAction(QIcon(I('default_cover.png')),
                            _('Mark %s as cover image') % n,
                            partial(self.mark_as_cover, cn))
            elif current_container(
            ).SUPPORTS_TITLEPAGES and mt in OEB_DOCS and cat == 'text':
                m.addAction(QIcon(I('default_cover.png')),
                            _('Mark %s as cover page') % n,
                            partial(self.mark_as_titlepage, cn))
            m.addSeparator()

        if num > 0:
            m.addSeparator()
            if num > 1:
                m.addAction(QIcon(I('modified.png')),
                            _('&Bulk rename selected files'),
                            self.request_bulk_rename)
            m.addAction(QIcon(I('trash.png')),
                        _('&Delete the %d selected file(s)') % num,
                        self.request_delete)
            m.addSeparator()

        selected_map = defaultdict(list)
        for item in sel:
            selected_map[unicode(item.data(
                0, CATEGORY_ROLE).toString())].append(
                    unicode(item.data(0, NAME_ROLE).toString()))

        for items in selected_map.itervalues():
            items.sort(key=self.index_of_name)

        if selected_map['text']:
            m.addAction(QIcon(I('format-text-color.png')),
                        _('Link &stylesheets...'),
                        partial(self.link_stylesheets, selected_map['text']))

        if len(selected_map['text']) > 1:
            m.addAction(
                QIcon(I('merge.png')), _('&Merge selected text files'),
                partial(self.start_merge, 'text', selected_map['text']))
        if len(selected_map['styles']) > 1:
            m.addAction(
                QIcon(I('merge.png')), _('&Merge selected style files'),
                partial(self.start_merge, 'styles', selected_map['styles']))

        if len(list(m.actions())) > 0:
            m.popup(self.mapToGlobal(point))
Beispiel #60
0
 def current_container(self):
     ' Return the current :class:`calibre.ebooks.oeb.polish.container.Container` object that represents the book being edited. '
     return current_container()