mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
2cf5b9e1da
@ -1,90 +0,0 @@
|
|||||||
import re
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
|
||||||
|
|
||||||
class GiveMeSomethingToRead(BasicNewsRecipe):
|
|
||||||
title = u'Give Me Something To Read'
|
|
||||||
description = 'Curation / aggregation of articles on diverse topics'
|
|
||||||
language = 'en'
|
|
||||||
__author__ = 'barty on mobileread.com forum'
|
|
||||||
max_articles_per_feed = 100
|
|
||||||
no_stylesheets = False
|
|
||||||
timefmt = ' [%a, %d %b, %Y]'
|
|
||||||
oldest_article = 365
|
|
||||||
auto_cleanup = True
|
|
||||||
INDEX = 'http://givemesomethingtoread.com'
|
|
||||||
CATEGORIES = [
|
|
||||||
# comment out categories you don't want
|
|
||||||
# (user friendly name, system name, max number of articles to load)
|
|
||||||
('The Arts','arts',25),
|
|
||||||
('Science','science',30),
|
|
||||||
('Technology','technology',30),
|
|
||||||
('Politics','politics',20),
|
|
||||||
('Media','media',30),
|
|
||||||
('Crime','crime',15),
|
|
||||||
('Other articles','',10)
|
|
||||||
]
|
|
||||||
|
|
||||||
def parse_index(self):
|
|
||||||
self.cover_url = 'http://thegretchenshow.files.wordpress.com/2009/12/well-read-cat-small.jpg'
|
|
||||||
feeds = []
|
|
||||||
seen_urls = set([])
|
|
||||||
regex = re.compile( r'http://(www\.)?([^/:]+)', re.I)
|
|
||||||
|
|
||||||
for category in self.CATEGORIES:
|
|
||||||
|
|
||||||
(cat_name, tag, max_articles) = category
|
|
||||||
|
|
||||||
tagurl = '' if tag=='' else '/tagged/'+tag
|
|
||||||
self.log('Reading category:', cat_name)
|
|
||||||
|
|
||||||
articles = []
|
|
||||||
pageno = 1
|
|
||||||
|
|
||||||
while len(articles) < max_articles and pageno < 100:
|
|
||||||
|
|
||||||
page = "%s%s/page/%d" % (self.INDEX, tagurl, pageno) if pageno > 1 else self.INDEX + tagurl
|
|
||||||
pageno += 1
|
|
||||||
|
|
||||||
self.log('\tReading page:', page)
|
|
||||||
try:
|
|
||||||
soup = self.index_to_soup(page)
|
|
||||||
except:
|
|
||||||
break
|
|
||||||
|
|
||||||
headers = soup.findAll('h2')
|
|
||||||
if len(headers) == .0:
|
|
||||||
break
|
|
||||||
|
|
||||||
for header in headers:
|
|
||||||
atag = header.find('a')
|
|
||||||
url = atag['href']
|
|
||||||
# skip promotionals and duplicate
|
|
||||||
if url.startswith('http://givemesomethingtoread') or url.startswith('/') or url in seen_urls:
|
|
||||||
continue
|
|
||||||
seen_urls.add(url)
|
|
||||||
title = self.tag_to_string(header)
|
|
||||||
self.log('\tFound article:', title)
|
|
||||||
#self.log('\t', url)
|
|
||||||
desc = header.parent.find('blockquote')
|
|
||||||
desc = self.tag_to_string(desc) if desc else ''
|
|
||||||
m = regex.match( url)
|
|
||||||
if m:
|
|
||||||
desc = "[%s] %s" % (m.group(2), desc)
|
|
||||||
#self.log('\t', desc)
|
|
||||||
date = ''
|
|
||||||
p = header.parent.previousSibling
|
|
||||||
# navigate up to find h3, which contains the date
|
|
||||||
while p:
|
|
||||||
if hasattr(p,'name') and p.name == 'h3':
|
|
||||||
date = self.tag_to_string(p)
|
|
||||||
break
|
|
||||||
p = p.previousSibling
|
|
||||||
articles.append({'title':title,'url':url,'description':desc,'date':date})
|
|
||||||
if len(articles) >= max_articles:
|
|
||||||
break
|
|
||||||
|
|
||||||
if articles:
|
|
||||||
feeds.append((cat_name, articles))
|
|
||||||
|
|
||||||
return feeds
|
|
||||||
|
|
@ -11,7 +11,8 @@ class PsychologyToday(BasicNewsRecipe):
|
|||||||
language = 'en'
|
language = 'en'
|
||||||
category = 'news'
|
category = 'news'
|
||||||
encoding = 'UTF-8'
|
encoding = 'UTF-8'
|
||||||
keep_only_tags = [dict(attrs={'class':['print-title', 'print-submitted', 'print-content', 'print-footer', 'print-source_url', 'print-links']})]
|
auto_cleanup = True
|
||||||
|
#keep_only_tags = [dict(attrs={'class':['print-title', 'print-submitted', 'print-content', 'print-footer', 'print-source_url', 'print-links']})]
|
||||||
no_javascript = True
|
no_javascript = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
|
||||||
@ -31,50 +32,32 @@ class PsychologyToday(BasicNewsRecipe):
|
|||||||
self.timefmt = u' [%s]'%date
|
self.timefmt = u' [%s]'%date
|
||||||
|
|
||||||
articles = []
|
articles = []
|
||||||
for post in div.findAll('div', attrs={'class':'collections-node-feature-info'}):
|
for post in div.findAll('div', attrs={'class':'collections-node-feature collection-node-even'}):
|
||||||
title = self.tag_to_string(post.find('h2'))
|
title = self.tag_to_string(post.find('h2'))
|
||||||
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
||||||
author = re.sub(r'.*by\s',"",self.tag_to_string(author_item).strip())
|
author = re.sub(r'.*by\s',"",self.tag_to_string(author_item).strip())
|
||||||
title = title + u' (%s)'%author
|
title = title + u' (%s)'%author
|
||||||
article_page= self.index_to_soup('http://www.psychologytoday.com'+post.find('a', href=True)['href'])
|
url= 'http://www.psychologytoday.com'+post.find('a', href=True)['href']
|
||||||
print_page=article_page.find('li', attrs={'class':'print_html first'})
|
#print_page=article_page.find('li', attrs={'class':'print_html first'})
|
||||||
url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
#url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
||||||
|
desc = self.tag_to_string(post.find('div', attrs={'class':'collection-node-description'})).strip()
|
||||||
|
self.log('Found article:', title)
|
||||||
|
self.log('\t', url)
|
||||||
|
self.log('\t', desc)
|
||||||
|
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
||||||
|
for post in div.findAll('div', attrs={'class':'collections-node-feature collection-node-odd'}):
|
||||||
|
title = self.tag_to_string(post.find('h2'))
|
||||||
|
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
||||||
|
author = re.sub(r'.*by\s',"",self.tag_to_string(author_item).strip())
|
||||||
|
title = title + u' (%s)'%author
|
||||||
|
url= 'http://www.psychologytoday.com'+post.find('a', href=True)['href']
|
||||||
|
#print_page=article_page.find('li', attrs={'class':'print_html first'})
|
||||||
|
#url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
||||||
desc = self.tag_to_string(post.find('div', attrs={'class':'collection-node-description'})).strip()
|
desc = self.tag_to_string(post.find('div', attrs={'class':'collection-node-description'})).strip()
|
||||||
self.log('Found article:', title)
|
self.log('Found article:', title)
|
||||||
self.log('\t', url)
|
self.log('\t', url)
|
||||||
self.log('\t', desc)
|
self.log('\t', desc)
|
||||||
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
||||||
|
|
||||||
for post in div.findAll('div', attrs={'class':'collections-node-thumbnail-info'}):
|
|
||||||
title = self.tag_to_string(post.find('h2'))
|
|
||||||
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
|
||||||
article_page= self.index_to_soup('http://www.psychologytoday.com'+post.find('a', href=True)['href'])
|
|
||||||
print_page=article_page.find('li', attrs={'class':'print_html first'})
|
|
||||||
description = post.find('div', attrs={'class':'collection-node-description'})
|
|
||||||
author = re.sub(r'.*by\s',"",self.tag_to_string(description.nextSibling).strip())
|
|
||||||
desc = self.tag_to_string(description).strip()
|
|
||||||
url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
|
||||||
title = title + u' (%s)'%author
|
|
||||||
self.log('Found article:', title)
|
|
||||||
self.log('\t', url)
|
|
||||||
self.log('\t', desc)
|
|
||||||
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
|
||||||
|
|
||||||
for post in div.findAll('li', attrs={'class':['collection-item-list-odd','collection-item-list-even']}):
|
|
||||||
title = self.tag_to_string(post.find('h2'))
|
|
||||||
author_item=post.find('div', attrs={'class':'collection-node-byline'})
|
|
||||||
author = re.sub(r'.*by\s',"",self.tag_to_string(author_item).strip())
|
|
||||||
title = title + u' (%s)'%author
|
|
||||||
article_page= self.index_to_soup('http://www.psychologytoday.com'+post.find('a', href=True)['href'])
|
|
||||||
print_page=article_page.find('li', attrs={'class':'print_html first'})
|
|
||||||
if print_page is not None:
|
|
||||||
url='http://www.psychologytoday.com'+print_page.find('a',href=True)['href']
|
|
||||||
desc = self.tag_to_string(post.find('div', attrs={'class':'collection-node-description'})).strip()
|
|
||||||
self.log('Found article:', title)
|
|
||||||
self.log('\t', url)
|
|
||||||
self.log('\t', desc)
|
|
||||||
articles.append({'title':title, 'url':url, 'date':'','description':desc})
|
|
||||||
|
|
||||||
return [('Current Issue', articles)]
|
return [('Current Issue', articles)]
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +50,10 @@ class ScienceNewsIssue(BasicNewsRecipe):
|
|||||||
dict(name='ul', attrs={'id':'toc'})
|
dict(name='ul', attrs={'id':'toc'})
|
||||||
]
|
]
|
||||||
|
|
||||||
|
remove_tags= [ dict(name='a', attrs={'class':'enlarge print-no'}),
|
||||||
|
dict(name='a', attrs={'rel':'shadowbox'})
|
||||||
|
]
|
||||||
|
|
||||||
feeds = [(u"Science News Current Issues", u'http://www.sciencenews.org/view/feed/type/edition/name/issues.rss')]
|
feeds = [(u"Science News Current Issues", u'http://www.sciencenews.org/view/feed/type/edition/name/issues.rss')]
|
||||||
|
|
||||||
match_regexps = [
|
match_regexps = [
|
||||||
@ -57,6 +61,12 @@ class ScienceNewsIssue(BasicNewsRecipe):
|
|||||||
r'www.sciencenews.org/view/generic/id'
|
r'www.sciencenews.org/view/generic/id'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def image_url_processor(self, baseurl, url):
|
||||||
|
x = url.split('/')
|
||||||
|
if x[4] == u'scale':
|
||||||
|
url = u'http://www.sciencenews.org/view/download/id/' + x[6] + u'/name/' + x[-1]
|
||||||
|
return url
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
cover_url = None
|
cover_url = None
|
||||||
index = 'http://www.sciencenews.org/view/home'
|
index = 'http://www.sciencenews.org/view/home'
|
||||||
@ -64,7 +74,6 @@ class ScienceNewsIssue(BasicNewsRecipe):
|
|||||||
link_item = soup.find(name = 'img',alt = "issue")
|
link_item = soup.find(name = 'img',alt = "issue")
|
||||||
if link_item:
|
if link_item:
|
||||||
cover_url = 'http://www.sciencenews.org' + link_item['src'] + '.jpg'
|
cover_url = 'http://www.sciencenews.org' + link_item['src'] + '.jpg'
|
||||||
|
|
||||||
return cover_url
|
return cover_url
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
|
11
recipes/the_feature.recipe
Normal file
11
recipes/the_feature.recipe
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1365777047(BasicNewsRecipe):
|
||||||
|
title = u'The Feature'
|
||||||
|
__author__ = 'Jose Pinto'
|
||||||
|
language = 'en'
|
||||||
|
oldest_article = 30
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
auto_cleanup = True
|
||||||
|
use_embedded_content = False
|
||||||
|
feeds = [(u'Latest', u'http://thefeature.net/rss/links')]
|
@ -448,8 +448,15 @@
|
|||||||
<xsl:template match = "rtf:field[@type='hyperlink']">
|
<xsl:template match = "rtf:field[@type='hyperlink']">
|
||||||
<xsl:element name ="a">
|
<xsl:element name ="a">
|
||||||
<xsl:attribute name = "href">
|
<xsl:attribute name = "href">
|
||||||
|
<xsl:choose>
|
||||||
|
<xsl:when test="@argument">
|
||||||
|
<xsl:value-of select="@argument"/>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:otherwise>
|
||||||
<xsl:if test = "not(contains(@link, '/'))">#</xsl:if>
|
<xsl:if test = "not(contains(@link, '/'))">#</xsl:if>
|
||||||
<xsl:value-of select = "@link"/>
|
<xsl:value-of select = "@link"/>
|
||||||
|
</xsl:otherwise>
|
||||||
|
</xsl:choose>
|
||||||
</xsl:attribute>
|
</xsl:attribute>
|
||||||
<xsl:apply-templates/>
|
<xsl:apply-templates/>
|
||||||
</xsl:element>
|
</xsl:element>
|
||||||
|
@ -175,7 +175,7 @@ def gui_polish(data):
|
|||||||
if not data.pop('metadata'):
|
if not data.pop('metadata'):
|
||||||
data.pop('opf')
|
data.pop('opf')
|
||||||
if not data.pop('do_cover'):
|
if not data.pop('do_cover'):
|
||||||
data.pop('cover')
|
data.pop('cover', None)
|
||||||
file_map = {x:x for x in files}
|
file_map = {x:x for x in files}
|
||||||
opts = ALL_OPTS.copy()
|
opts = ALL_OPTS.copy()
|
||||||
opts.update(data)
|
opts.update(data)
|
||||||
|
@ -716,10 +716,11 @@ def choose_save_file(window, name, title, filters=[], all_files=True):
|
|||||||
ans = ans[0]
|
ans = ans[0]
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def choose_images(window, name, title, select_only_single_file=True):
|
def choose_images(window, name, title, select_only_single_file=True,
|
||||||
|
formats=('png', 'gif', 'jpg', 'jpeg', 'svg')):
|
||||||
mode = QFileDialog.ExistingFile if select_only_single_file else QFileDialog.ExistingFiles
|
mode = QFileDialog.ExistingFile if select_only_single_file else QFileDialog.ExistingFiles
|
||||||
fd = FileDialog(title=title, name=name,
|
fd = FileDialog(title=title, name=name,
|
||||||
filters=[('Images', ['png', 'gif', 'jpeg', 'jpg', 'svg'])],
|
filters=[('Images', list(formats))],
|
||||||
parent=window, add_all_files_filter=False, mode=mode,
|
parent=window, add_all_files_filter=False, mode=mode,
|
||||||
)
|
)
|
||||||
fd.setParent(None)
|
fd.setParent(None)
|
||||||
|
@ -17,7 +17,7 @@ from PyQt4.Qt import (QDialog, QGridLayout, QIcon, QCheckBox, QLabel, QFrame,
|
|||||||
QSizePolicy, QTimer, QModelIndex, QTextEdit,
|
QSizePolicy, QTimer, QModelIndex, QTextEdit,
|
||||||
QInputDialog, QMenu)
|
QInputDialog, QMenu)
|
||||||
|
|
||||||
from calibre.gui2 import error_dialog, Dispatcher, gprefs
|
from calibre.gui2 import error_dialog, Dispatcher, gprefs, question_dialog
|
||||||
from calibre.gui2.actions import InterfaceAction
|
from calibre.gui2.actions import InterfaceAction
|
||||||
from calibre.gui2.convert.metadata import create_opf_file
|
from calibre.gui2.convert.metadata import create_opf_file
|
||||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||||
@ -204,6 +204,15 @@ class Polish(QDialog): # {{{
|
|||||||
ac[action] = saved_prefs[action] = bool(getattr(self, 'opt_'+action).isChecked())
|
ac[action] = saved_prefs[action] = bool(getattr(self, 'opt_'+action).isChecked())
|
||||||
if ac[action]:
|
if ac[action]:
|
||||||
something = True
|
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 not something:
|
if not something:
|
||||||
return error_dialog(self, _('No actions selected'),
|
return error_dialog(self, _('No actions selected'),
|
||||||
_('You must select at least one action, or click Cancel.'),
|
_('You must select at least one action, or click Cancel.'),
|
||||||
@ -476,8 +485,7 @@ class PolishAction(InterfaceAction):
|
|||||||
db.save_original_format(book_id, fmt, notify=False)
|
db.save_original_format(book_id, fmt, notify=False)
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
db.add_format(book_id, fmt, f, index_is_id=True)
|
db.add_format(book_id, fmt, f, index_is_id=True)
|
||||||
self.gui.status_bar.show_message(job.description + \
|
self.gui.status_bar.show_message(job.description + (' completed'), 2000)
|
||||||
(' completed'), 2000)
|
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(base)
|
shutil.rmtree(base)
|
||||||
parent = os.path.dirname(base)
|
parent = os.path.dirname(base)
|
||||||
|
@ -927,9 +927,9 @@ class Cover(ImageView): # {{{
|
|||||||
return sz
|
return sz
|
||||||
|
|
||||||
def select_cover(self, *args):
|
def select_cover(self, *args):
|
||||||
files = choose_images(self, 'change cover dialog',
|
files = choose_images(
|
||||||
_('Choose cover for ') +
|
self, 'change cover dialog', _('Choose cover for ') + self.dialog.title.current_val,
|
||||||
self.dialog.title.current_val)
|
formats=('png', 'gif', 'jpg', 'jpeg'))
|
||||||
if not files:
|
if not files:
|
||||||
return
|
return
|
||||||
_file = files[0]
|
_file = files[0]
|
||||||
|
@ -11,11 +11,11 @@ from base64 import b64encode
|
|||||||
|
|
||||||
from PyQt4.Qt import (QWidget, QGridLayout, QListWidget, QSize, Qt, QUrl,
|
from PyQt4.Qt import (QWidget, QGridLayout, QListWidget, QSize, Qt, QUrl,
|
||||||
pyqtSlot, pyqtSignal, QVBoxLayout, QFrame, QLabel,
|
pyqtSlot, pyqtSignal, QVBoxLayout, QFrame, QLabel,
|
||||||
QLineEdit, QTimer, QPushButton, QIcon)
|
QLineEdit, QTimer, QPushButton, QIcon, QSplitter)
|
||||||
from PyQt4.QtWebKit import QWebView, QWebPage, QWebElement
|
from PyQt4.QtWebKit import QWebView, QWebPage, QWebElement
|
||||||
|
|
||||||
from calibre.ebooks.oeb.display.webview import load_html
|
from calibre.ebooks.oeb.display.webview import load_html
|
||||||
from calibre.gui2 import error_dialog, question_dialog
|
from calibre.gui2 import error_dialog, question_dialog, gprefs
|
||||||
from calibre.utils.logging import default_log
|
from calibre.utils.logging import default_log
|
||||||
|
|
||||||
class Page(QWebPage): # {{{
|
class Page(QWebPage): # {{{
|
||||||
@ -106,38 +106,46 @@ class ItemEdit(QWidget):
|
|||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QWidget.__init__(self, parent)
|
QWidget.__init__(self, parent)
|
||||||
self.l = l = QGridLayout()
|
self.setLayout(QVBoxLayout())
|
||||||
self.setLayout(l)
|
|
||||||
|
|
||||||
self.la = la = QLabel('<b>'+_(
|
self.la = la = QLabel('<b>'+_(
|
||||||
'Select a destination for the Table of Contents entry'))
|
'Select a destination for the Table of Contents entry'))
|
||||||
l.addWidget(la, 0, 0, 1, 3)
|
self.layout().addWidget(la)
|
||||||
|
self.splitter = sp = QSplitter(self)
|
||||||
|
self.layout().addWidget(sp)
|
||||||
|
self.layout().setStretch(1, 10)
|
||||||
|
sp.setOpaqueResize(False)
|
||||||
|
sp.setChildrenCollapsible(False)
|
||||||
|
|
||||||
self.dest_list = dl = QListWidget(self)
|
self.dest_list = dl = QListWidget(self)
|
||||||
dl.setMinimumWidth(250)
|
dl.setMinimumWidth(250)
|
||||||
dl.currentItemChanged.connect(self.current_changed)
|
dl.currentItemChanged.connect(self.current_changed)
|
||||||
l.addWidget(dl, 1, 0, 2, 1)
|
sp.addWidget(dl)
|
||||||
|
|
||||||
|
w = self.w = QWidget(self)
|
||||||
|
l = w.l = QGridLayout()
|
||||||
|
w.setLayout(l)
|
||||||
self.view = WebView(self)
|
self.view = WebView(self)
|
||||||
self.view.elem_clicked.connect(self.elem_clicked)
|
self.view.elem_clicked.connect(self.elem_clicked)
|
||||||
l.addWidget(self.view, 1, 1, 1, 3)
|
l.addWidget(self.view, 0, 0, 1, 3)
|
||||||
|
sp.addWidget(w)
|
||||||
|
|
||||||
|
self.search_text = s = QLineEdit(self)
|
||||||
|
s.setPlaceholderText(_('Search for text...'))
|
||||||
|
l.addWidget(s, 1, 0)
|
||||||
|
self.ns_button = b = QPushButton(QIcon(I('arrow-down.png')), _('Find &next'), self)
|
||||||
|
b.clicked.connect(self.find_next)
|
||||||
|
l.addWidget(b, 1, 1)
|
||||||
|
self.ps_button = b = QPushButton(QIcon(I('arrow-up.png')), _('Find &previous'), self)
|
||||||
|
l.addWidget(b, 1, 2)
|
||||||
|
b.clicked.connect(self.find_previous)
|
||||||
|
|
||||||
self.f = f = QFrame()
|
self.f = f = QFrame()
|
||||||
f.setFrameShape(f.StyledPanel)
|
f.setFrameShape(f.StyledPanel)
|
||||||
f.setMinimumWidth(250)
|
f.setMinimumWidth(250)
|
||||||
l.addWidget(f, 1, 4, 2, 1)
|
|
||||||
self.search_text = s = QLineEdit(self)
|
|
||||||
s.setPlaceholderText(_('Search for text...'))
|
|
||||||
l.addWidget(s, 2, 1, 1, 1)
|
|
||||||
self.ns_button = b = QPushButton(QIcon(I('arrow-down.png')), _('Find &next'), self)
|
|
||||||
b.clicked.connect(self.find_next)
|
|
||||||
l.addWidget(b, 2, 2, 1, 1)
|
|
||||||
self.ps_button = b = QPushButton(QIcon(I('arrow-up.png')), _('Find &previous'), self)
|
|
||||||
l.addWidget(b, 2, 3, 1, 1)
|
|
||||||
b.clicked.connect(self.find_previous)
|
|
||||||
l.setRowStretch(1, 10)
|
|
||||||
l = f.l = QVBoxLayout()
|
l = f.l = QVBoxLayout()
|
||||||
f.setLayout(l)
|
f.setLayout(l)
|
||||||
|
sp.addWidget(f)
|
||||||
|
|
||||||
f.la = la = QLabel('<p>'+_(
|
f.la = la = QLabel('<p>'+_(
|
||||||
'Here you can choose a destination for the Table of Contents\' entry'
|
'Here you can choose a destination for the Table of Contents\' entry'
|
||||||
@ -167,6 +175,10 @@ class ItemEdit(QWidget):
|
|||||||
|
|
||||||
l.addStretch()
|
l.addStretch()
|
||||||
|
|
||||||
|
state = gprefs.get('toc_edit_splitter_state', None)
|
||||||
|
if state is not None:
|
||||||
|
sp.restoreState(state)
|
||||||
|
|
||||||
def keyPressEvent(self, ev):
|
def keyPressEvent(self, ev):
|
||||||
if ev.key() in (Qt.Key_Return, Qt.Key_Enter) and self.search_text.hasFocus():
|
if ev.key() in (Qt.Key_Return, Qt.Key_Enter) and self.search_text.hasFocus():
|
||||||
# Prevent pressing enter in the search box from triggering the dialog's accept() method
|
# Prevent pressing enter in the search box from triggering the dialog's accept() method
|
||||||
@ -236,6 +248,7 @@ class ItemEdit(QWidget):
|
|||||||
if item is not None:
|
if item is not None:
|
||||||
if where is None:
|
if where is None:
|
||||||
self.name.setText(item.data(0, Qt.DisplayRole).toString())
|
self.name.setText(item.data(0, Qt.DisplayRole).toString())
|
||||||
|
self.name.setCursorPosition(0)
|
||||||
toc = item.data(0, Qt.UserRole).toPyObject()
|
toc = item.data(0, Qt.UserRole).toPyObject()
|
||||||
if toc.dest:
|
if toc.dest:
|
||||||
for i in xrange(self.dest_list.count()):
|
for i in xrange(self.dest_list.count()):
|
||||||
@ -272,7 +285,6 @@ class ItemEdit(QWidget):
|
|||||||
loctext = _('Approximately %d%% from the top')%frac
|
loctext = _('Approximately %d%% from the top')%frac
|
||||||
return loctext
|
return loctext
|
||||||
|
|
||||||
|
|
||||||
def elem_clicked(self, tag, frac, elem_id, loc):
|
def elem_clicked(self, tag, frac, elem_id, loc):
|
||||||
self.current_frag = elem_id or loc
|
self.current_frag = elem_id or loc
|
||||||
base = _('Location: A <%s> tag inside the file')%tag
|
base = _('Location: A <%s> tag inside the file')%tag
|
||||||
|
@ -14,7 +14,7 @@ from functools import partial
|
|||||||
from PyQt4.Qt import (QPushButton, QFrame, QVariant, QMenu, QInputDialog,
|
from PyQt4.Qt import (QPushButton, QFrame, QVariant, QMenu, QInputDialog,
|
||||||
QDialog, QVBoxLayout, QDialogButtonBox, QSize, QStackedWidget, QWidget,
|
QDialog, QVBoxLayout, QDialogButtonBox, QSize, QStackedWidget, QWidget,
|
||||||
QLabel, Qt, pyqtSignal, QIcon, QTreeWidget, QGridLayout, QTreeWidgetItem,
|
QLabel, Qt, pyqtSignal, QIcon, QTreeWidget, QGridLayout, QTreeWidgetItem,
|
||||||
QToolButton, QItemSelectionModel, QCursor)
|
QToolButton, QItemSelectionModel, QCursor, QKeySequence)
|
||||||
|
|
||||||
from calibre.ebooks.oeb.polish.container import get_container, AZW3Container
|
from calibre.ebooks.oeb.polish.container import get_container, AZW3Container
|
||||||
from calibre.ebooks.oeb.polish.toc import (
|
from calibre.ebooks.oeb.polish.toc import (
|
||||||
@ -207,7 +207,6 @@ class ItemView(QFrame): # {{{
|
|||||||
)))
|
)))
|
||||||
l.addWidget(b)
|
l.addWidget(b)
|
||||||
|
|
||||||
|
|
||||||
l.addStretch()
|
l.addStretch()
|
||||||
self.w1 = la = QLabel(_('<b>WARNING:</b> calibre only supports the '
|
self.w1 = la = QLabel(_('<b>WARNING:</b> calibre only supports the '
|
||||||
'creation of linear ToCs in AZW3 files. In a '
|
'creation of linear ToCs in AZW3 files. In a '
|
||||||
@ -351,6 +350,8 @@ class ItemView(QFrame): # {{{
|
|||||||
|
|
||||||
class TreeWidget(QTreeWidget): # {{{
|
class TreeWidget(QTreeWidget): # {{{
|
||||||
|
|
||||||
|
edit_item = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QTreeWidget.__init__(self, parent)
|
QTreeWidget.__init__(self, parent)
|
||||||
self.setHeaderLabel(_('Table of Contents'))
|
self.setHeaderLabel(_('Table of Contents'))
|
||||||
@ -510,20 +511,25 @@ class TreeWidget(QTreeWidget): # {{{
|
|||||||
|
|
||||||
def show_context_menu(self, point):
|
def show_context_menu(self, point):
|
||||||
item = self.currentItem()
|
item = self.currentItem()
|
||||||
|
def key(k):
|
||||||
|
sc = unicode(QKeySequence(k | Qt.CTRL).toString(QKeySequence.NativeText))
|
||||||
|
return ' [%s]'%sc
|
||||||
|
|
||||||
if item is not None:
|
if item is not None:
|
||||||
m = QMenu()
|
m = QMenu()
|
||||||
ci = unicode(item.data(0, Qt.DisplayRole).toString())
|
ci = unicode(item.data(0, Qt.DisplayRole).toString())
|
||||||
p = item.parent() or self.invisibleRootItem()
|
p = item.parent() or self.invisibleRootItem()
|
||||||
idx = p.indexOfChild(item)
|
idx = p.indexOfChild(item)
|
||||||
if idx > 0:
|
if idx > 0:
|
||||||
m.addAction(QIcon(I('arrow-up.png')), _('Move "%s" up')%ci, self.move_up)
|
m.addAction(QIcon(I('arrow-up.png')), (_('Move "%s" up')%ci)+key(Qt.Key_Up), self.move_up)
|
||||||
if idx + 1 < p.childCount():
|
if idx + 1 < p.childCount():
|
||||||
m.addAction(QIcon(I('arrow-down.png')), _('Move "%s" down')%ci, self.move_down)
|
m.addAction(QIcon(I('arrow-down.png')), (_('Move "%s" down')%ci)+key(Qt.Key_Down), self.move_down)
|
||||||
m.addAction(QIcon(I('trash.png')), _('Remove all selected items'), self.del_items)
|
m.addAction(QIcon(I('trash.png')), _('Remove all selected items'), self.del_items)
|
||||||
if item.parent() is not None:
|
if item.parent() is not None:
|
||||||
m.addAction(QIcon(I('back.png')), _('Unindent "%s"')%ci, self.move_left)
|
m.addAction(QIcon(I('back.png')), (_('Unindent "%s"')%ci)+key(Qt.Key_Left), self.move_left)
|
||||||
if idx > 0:
|
if idx > 0:
|
||||||
m.addAction(QIcon(I('forward.png')), _('Indent "%s"')%ci, self.move_right)
|
m.addAction(QIcon(I('forward.png')), (_('Indent "%s"')%ci)+key(Qt.Key_Right), self.move_right)
|
||||||
|
m.addAction(QIcon(I('edit_input.png')), _('Change the location this entry points to'), self.edit_item)
|
||||||
m.addAction(_('Change all selected items to title case'), self.title_case)
|
m.addAction(_('Change all selected items to title case'), self.title_case)
|
||||||
m.exec_(QCursor.pos())
|
m.exec_(QCursor.pos())
|
||||||
# }}}
|
# }}}
|
||||||
@ -537,6 +543,7 @@ class TOCView(QWidget): # {{{
|
|||||||
l = self.l = QGridLayout()
|
l = self.l = QGridLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
self.tocw = t = TreeWidget(self)
|
self.tocw = t = TreeWidget(self)
|
||||||
|
self.tocw.edit_item.connect(self.edit_item)
|
||||||
l.addWidget(t, 0, 0, 7, 3)
|
l.addWidget(t, 0, 0, 7, 3)
|
||||||
self.up_button = b = QToolButton(self)
|
self.up_button = b = QToolButton(self)
|
||||||
b.setIcon(QIcon(I('arrow-up.png')))
|
b.setIcon(QIcon(I('arrow-up.png')))
|
||||||
@ -595,6 +602,9 @@ class TOCView(QWidget): # {{{
|
|||||||
|
|
||||||
l.setColumnStretch(2, 10)
|
l.setColumnStretch(2, 10)
|
||||||
|
|
||||||
|
def edit_item(self):
|
||||||
|
self.item_view.edit_item()
|
||||||
|
|
||||||
def event(self, e):
|
def event(self, e):
|
||||||
if e.type() == e.StatusTip:
|
if e.type() == e.StatusTip:
|
||||||
txt = unicode(e.tip()) or self.default_msg
|
txt = unicode(e.tip()) or self.default_msg
|
||||||
@ -742,12 +752,12 @@ class TOCView(QWidget): # {{{
|
|||||||
else:
|
else:
|
||||||
parent = item.parent() or self.root
|
parent = item.parent() or self.root
|
||||||
idx = parent.indexOfChild(item)
|
idx = parent.indexOfChild(item)
|
||||||
if where == 'after': idx += 1
|
if where == 'after':
|
||||||
|
idx += 1
|
||||||
c = self.create_item(parent, child, idx=idx)
|
c = self.create_item(parent, child, idx=idx)
|
||||||
self.tocw.setCurrentItem(c, 0, QItemSelectionModel.ClearAndSelect)
|
self.tocw.setCurrentItem(c, 0, QItemSelectionModel.ClearAndSelect)
|
||||||
self.tocw.scrollToItem(c)
|
self.tocw.scrollToItem(c)
|
||||||
|
|
||||||
|
|
||||||
def create_toc(self):
|
def create_toc(self):
|
||||||
root = TOC()
|
root = TOC()
|
||||||
|
|
||||||
@ -857,6 +867,7 @@ class TOCEditor(QDialog): # {{{
|
|||||||
def accept(self):
|
def accept(self):
|
||||||
if self.stacks.currentIndex() == 2:
|
if self.stacks.currentIndex() == 2:
|
||||||
self.toc_view.update_item(*self.item_edit.result)
|
self.toc_view.update_item(*self.item_edit.result)
|
||||||
|
gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState())
|
||||||
self.stacks.setCurrentIndex(1)
|
self.stacks.setCurrentIndex(1)
|
||||||
elif self.stacks.currentIndex() == 1:
|
elif self.stacks.currentIndex() == 1:
|
||||||
self.working = False
|
self.working = False
|
||||||
@ -883,6 +894,7 @@ class TOCEditor(QDialog): # {{{
|
|||||||
if not self.bb.isEnabled():
|
if not self.bb.isEnabled():
|
||||||
return
|
return
|
||||||
if self.stacks.currentIndex() == 2:
|
if self.stacks.currentIndex() == 2:
|
||||||
|
gprefs['toc_edit_splitter_state'] = bytearray(self.item_edit.splitter.saveState())
|
||||||
self.stacks.setCurrentIndex(1)
|
self.stacks.setCurrentIndex(1)
|
||||||
else:
|
else:
|
||||||
self.working = False
|
self.working = False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user