diff --git a/recipes/ivanamilakovic.recipe b/recipes/ivanamilakovic.recipe new file mode 100644 index 0000000000..34e00a7ed8 --- /dev/null +++ b/recipes/ivanamilakovic.recipe @@ -0,0 +1,43 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2012, Darko Miletic ' +''' +ivanamilakovic.blogspot.com +''' + +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class IvanaMilakovic(BasicNewsRecipe): + title = u'Ivana Milaković' + __author__ = 'Darko Miletic' + description = u'Hronika mačijeg škrabala - priče, inspiracija, knjige, pisanje, prevodi...' + oldest_article = 80 + max_articles_per_feed = 100 + language = 'sr' + encoding = 'utf-8' + no_stylesheets = True + use_embedded_content = True + publication_type = 'blog' + extra_css = """ + @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} + body{font-family: Arial,Tahoma,Helvetica,FreeSans,sans1,sans-serif} + img{margin-bottom: 0.8em; border: 1px solid #333333; padding: 4px } + """ + + conversion_options = { + 'comment' : description + , 'tags' : 'knjige, blog, srbija, sf' + , 'publisher': 'Ivana Milakovic' + , 'language' : language + } + + preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] + + feeds = [(u'Posts', u'http://ivanamilakovic.blogspot.com/feeds/posts/default')] + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return self.adeify_images(soup) diff --git a/recipes/klubknjige.recipe b/recipes/klubknjige.recipe new file mode 100644 index 0000000000..dd16c0b3b9 --- /dev/null +++ b/recipes/klubknjige.recipe @@ -0,0 +1,42 @@ + +__license__ = 'GPL v3' +__copyright__ = '2012, Darko Miletic ' +''' +klub-knjige.blogspot.com +''' + +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class KlubKnjige(BasicNewsRecipe): + title = 'Klub knjige' + __author__ = 'Darko Miletic' + description = 'literarni blog' + oldest_article = 30 + max_articles_per_feed = 100 + language = 'sr' + encoding = 'utf-8' + no_stylesheets = True + use_embedded_content = True + publication_type = 'blog' + extra_css = """ + @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} + body{font-family: Arial,Tahoma,Helvetica,FreeSans,sans1,sans-serif} + img{margin-bottom: 0.8em; border: 1px solid #333333; padding: 4px } + """ + + conversion_options = { + 'comment' : description + , 'tags' : 'knjige, blog, srbija, sf' + , 'publisher': 'Klub Knjige' + , 'language' : language + } + + preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] + + feeds = [(u'Posts', u'http://klub-knjige.blogspot.com/feeds/posts/default')] + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return self.adeify_images(soup) diff --git a/recipes/le_monde.recipe b/recipes/le_monde.recipe index 8fcdf9c870..6c7f15cca7 100644 --- a/recipes/le_monde.recipe +++ b/recipes/le_monde.recipe @@ -3,7 +3,6 @@ __copyright__ = '2011' ''' lemonde.fr ''' -import re from calibre.web.feeds.recipes import BasicNewsRecipe class LeMonde(BasicNewsRecipe): @@ -41,77 +40,8 @@ class LeMonde(BasicNewsRecipe): remove_empty_feeds = True - filterDuplicates = True + auto_cleanup = True - def preprocess_html(self, soup): - for alink in soup.findAll('a'): - if alink.string is not None: - tstr = alink.string - alink.replaceWith(tstr) - return self.adeify_images(soup) - - preprocess_regexps = [ - (re.compile(r'([0-9])%'), lambda m: m.group(1) + ' %'), - (re.compile(r'([0-9])([0-9])([0-9]) ([0-9])([0-9])([0-9])'), lambda m: m.group(1) + m.group(2) + m.group(3) + ' ' + m.group(4) + m.group(5) + m.group(6)), - (re.compile(r'([0-9]) ([0-9])([0-9])([0-9])'), lambda m: m.group(1) + ' ' + m.group(2) + m.group(3) + m.group(4)), - (re.compile(r''), lambda match: ' '), - (re.compile(r'\("'), lambda match: '(« '), - (re.compile(r'"\)'), lambda match: ' »)'), - (re.compile(r'“'), lambda match: '(« '), - (re.compile(r'”'), lambda match: ' »)'), - (re.compile(r'>\''), lambda match: '>‘'), - (re.compile(r' \''), lambda match: ' ‘'), - (re.compile(r'\''), lambda match: '’'), - (re.compile(r'"'), lambda match: '« '), - (re.compile(r'""'), lambda match: '« '), - (re.compile(r'""'), lambda match: ' »'), - (re.compile(r'"'), lambda match: ' »'), - (re.compile(r'""'), lambda match: '>« '), - (re.compile(r'"<'), lambda match: ' »<'), - (re.compile(r'’"'), lambda match: '’« '), - (re.compile(r' "'), lambda match: ' « '), - (re.compile(r'" '), lambda match: ' » '), - (re.compile(r'"\.'), lambda match: ' ».'), - (re.compile(r'",'), lambda match: ' »,'), - (re.compile(r'"\?'), lambda match: ' »?'), - (re.compile(r'":'), lambda match: ' »:'), - (re.compile(r'";'), lambda match: ' »;'), - (re.compile(r'"\!'), lambda match: ' »!'), - (re.compile(r' :'), lambda match: ' :'), - (re.compile(r' ;'), lambda match: ' ;'), - (re.compile(r' \?'), lambda match: ' ?'), - (re.compile(r' \!'), lambda match: ' !'), - (re.compile(r'\s»'), lambda match: ' »'), - (re.compile(r'«\s'), lambda match: '« '), - (re.compile(r' %'), lambda match: ' %'), - (re.compile(r'\.jpg » border='), lambda match: '.jpg'), - (re.compile(r'\.png » border='), lambda match: '.png'), - (re.compile(r' – '), lambda match: ' – '), - (re.compile(r' – '), lambda match: ' – '), - (re.compile(r' - '), lambda match: ' – '), - (re.compile(r' -,'), lambda match: ' –,'), - (re.compile(r'»:'), lambda match: '» :'), - ] - - - keep_only_tags = [ - dict(name='div', attrs={'class':['contenu']}) - ] - remove_tags = [dict(name='div', attrs={'class':['LM_atome']})] - remove_tags_after = [dict(id='appel_temoignage')] - - def get_article_url(self, article): - url = article.get('guid', None) - if '/chat/' in url or '.blog' in url or '/video/' in url or '/sport/' in url or '/portfolio/' in url or '/visuel/' in url : - url = None - return url - -# def get_article_url(self, article): -# link = article.get('link') -# if 'blog' not in link and ('chat' not in link): -# return link feeds = [ ('A la une', 'http://www.lemonde.fr/rss/une.xml'), @@ -137,3 +67,10 @@ class LeMonde(BasicNewsRecipe): return cover_url + def get_article_url(self, article): + url = article.get('guid', None) + if '/chat/' in url or '.blog' in url or '/video/' in url or '/sport/' in url or '/portfolio/' in url or '/visuel/' in url : + url = None + return url + + diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index ea5e4858ca..2a2242a68f 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -381,12 +381,15 @@ def browser(honor_time=True, max_time=2, mobile_browser=False, user_agent=None): user_agent = USER_AGENT_MOBILE if mobile_browser else USER_AGENT opener.addheaders = [('User-agent', user_agent)] proxies = get_proxies() + to_add = {} http_proxy = proxies.get('http', None) if http_proxy: - opener.set_proxies({'http':http_proxy}) + to_add['http'] = http_proxy https_proxy = proxies.get('https', None) if https_proxy: - opener.set_proxies({'https':https_proxy}) + to_add['https'] = https_proxy + if to_add: + opener.set_proxies(to_add) return opener diff --git a/src/calibre/gui2/viewer/config.ui b/src/calibre/gui2/viewer/config.ui index 3158241f28..f876b87fc3 100644 --- a/src/calibre/gui2/viewer/config.ui +++ b/src/calibre/gui2/viewer/config.ui @@ -255,7 +255,10 @@ - + + + Set the maximum width that the book's text and pictures will take when in fullscreen mode. This allows you to read the book text without it becoming too wide. + px @@ -270,10 +273,10 @@ - Maximum &view width: + Maximum text width in &fullscreen: - max_view_width + max_fs_width @@ -350,7 +353,7 @@ serif_family sans_family mono_family - max_view_width + max_fs_width opt_remember_window_size buttonBox diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index 2f520c1912..7999458004 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -12,7 +12,7 @@ from PyQt4.Qt import (QSize, QSizePolicy, QUrl, SIGNAL, Qt, QTimer, QPainter, QPalette, QBrush, QFontDatabase, QDialog, QColor, QPoint, QImage, QRegion, QVariant, QIcon, QFont, pyqtSignature, QAction, QByteArray, QMenu, - pyqtSignal, QSwipeGesture) + pyqtSignal, QSwipeGesture, QApplication) from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings from calibre.utils.config import Config, StringConfig @@ -46,8 +46,10 @@ def config(defaults=None): help=_('Remember last used window size')) c.add_opt('user_css', default='', help=_('Set the user CSS stylesheet. This can be used to customize the look of all books.')) - c.add_opt('max_view_width', default=6000, - help=_('Maximum width of the viewer window, in pixels.')) + c.add_opt('max_fs_width', default=800, + help=_("Set the maximum width that the book's text and pictures will take" + " when in fullscreen mode. This allows you to read the book text" + " without it becoming too wide.")) c.add_opt('fit_images', default=True, help=_('Resize images larger than the viewer window to fit inside it')) c.add_opt('hyphenate', default=False, help=_('Hyphenate text')) @@ -101,7 +103,7 @@ class ConfigDialog(QDialog, Ui_Dialog): self.standard_font.setCurrentIndex({'serif':0, 'sans':1, 'mono':2}[opts.standard_font]) self.css.setPlainText(opts.user_css) self.css.setToolTip(_('Set the user CSS stylesheet. This can be used to customize the look of all books.')) - self.max_view_width.setValue(opts.max_view_width) + self.max_fs_width.setValue(opts.max_fs_width) with zipfile.ZipFile(P('viewer/hyphenate/patterns.zip', allow_user_override=False), 'r') as zf: pats = [x.split('.')[0].replace('-', '_') for x in zf.namelist()] @@ -144,7 +146,7 @@ class ConfigDialog(QDialog, Ui_Dialog): c.set('user_css', unicode(self.css.toPlainText())) c.set('remember_window_size', self.opt_remember_window_size.isChecked()) c.set('fit_images', self.opt_fit_images.isChecked()) - c.set('max_view_width', int(self.max_view_width.value())) + c.set('max_fs_width', int(self.max_fs_width.value())) c.set('hyphenate', self.hyphenate.isChecked()) c.set('remember_current_page', self.opt_remember_current_page.isChecked()) c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked()) @@ -192,6 +194,8 @@ class Document(QWebPage): # {{{ self.loaded_javascript = False self.js_loader = JavaScriptLoader( dynamic_coffeescript=self.debug_javascript) + self.initial_left_margin = self.initial_right_margin = u'' + self.in_fullscreen_mode = False self.setLinkDelegationPolicy(self.DelegateAllLinks) self.scroll_marks = [] @@ -239,6 +243,9 @@ class Document(QWebPage): # {{{ self.enable_page_flip = self.page_flip_duration > 0.1 self.font_magnification_step = opts.font_magnification_step self.wheel_flips_pages = opts.wheel_flips_pages + screen_width = QApplication.desktop().screenGeometry().width() + # Leave some space for the scrollbar and some border + self.max_fs_width = min(opts.max_fs_width, screen_width-50) def fit_images(self): if self.do_fit_images: @@ -274,6 +281,30 @@ class Document(QWebPage): # {{{ self.set_bottom_padding(0) self.fit_images() self.init_hyphenate() + self.initial_left_margin = unicode(self.javascript( + 'document.body.style.marginLeft').toString()) + self.initial_right_margin = unicode(self.javascript( + 'document.body.style.marginRight').toString()) + if self.in_fullscreen_mode: + self.switch_to_fullscreen_mode() + + def switch_to_fullscreen_mode(self): + self.in_fullscreen_mode = True + self.javascript(''' + var s = document.body.style; + s.maxWidth = "%dpx"; + s.marginLeft = "auto"; + s.marginRight = "auto"; + '''%self.max_fs_width) + + def switch_to_window_mode(self): + self.in_fullscreen_mode = False + self.javascript(''' + var s = document.body.style; + s.maxWidth = "none"; + s.marginLeft = "%s"; + s.marginRight = "%s"; + '''%(self.initial_left_margin, self.initial_right_margin)) @pyqtSignature("QString") def debug(self, msg): @@ -581,8 +612,8 @@ class DocumentView(QWebView): # {{{ def config(self, parent=None): self.document.do_config(parent) - if self.manager is not None: - self.manager.set_max_width() + if self.document.in_fullscreen_mode: + self.document.switch_to_fullscreen_mode() self.setFocus(Qt.OtherFocusReason) def bookmark(self): @@ -602,6 +633,9 @@ class DocumentView(QWebView): # {{{ menu.insertAction(list(menu.actions())[0], self.search_action) menu.addSeparator() menu.addAction(self.goto_location_action) + if self.document.in_fullscreen_mode and self.manager is not None: + menu.addSeparator() + menu.addAction(self.manager.toggle_toolbar_action) menu.exec_(ev.globalPos()) def lookup(self, *args): diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 64521ecdd7..c1cb89aeb6 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -5,11 +5,11 @@ import traceback, os, sys, functools, collections, re from functools import partial from threading import Thread -from PyQt4.Qt import QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray, \ - QDoubleSpinBox, QLabel, QTextBrowser, \ - QPainter, QBrush, QColor, QStandardItemModel, QPalette, \ - QStandardItem, QUrl, QRegExpValidator, QRegExp, QLineEdit, \ - QToolButton, QMenu, QInputDialog, QAction, QKeySequence +from PyQt4.Qt import (QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray, + QSize, QDoubleSpinBox, QLabel, QTextBrowser, QPropertyAnimation, + QPainter, QBrush, QColor, QStandardItemModel, QPalette, QStandardItem, + QUrl, QRegExpValidator, QRegExp, QLineEdit, QToolButton, QMenu, + QInputDialog, QAction, QKeySequence) from calibre.gui2.viewer.main_ui import Ui_EbookViewer from calibre.gui2.viewer.printing import Printing @@ -55,8 +55,6 @@ class TOC(QStandardItemModel): self.appendRow(TOCItem(t)) self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents'))) - - class Worker(Thread): def run(self): @@ -292,6 +290,37 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup) self.action_full_screen.setCheckable(True) + self.full_screen_label = QLabel(''' +
+

%s

+

%s

+

%s

+
+ '''%(_('Full screen mode'), + _('Right click to show controls'), + _('Press Esc to quit')), + self) + self.full_screen_label.setVisible(False) + self.full_screen_label.setStyleSheet(''' + QLabel { + text-align: center; + background-color: white; + color: black; + border-width: 1px; + border-style: solid; + border-radius: 20px; + } + ''') + self.toggle_toolbar_action = QAction(_('Show/hide controls'), self) + self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars) + self.addAction(self.toggle_toolbar_action) + self.full_screen_label_anim = QPropertyAnimation( + self.full_screen_label, 'size') + self.esc_full_screen_action = a = QAction(self) + self.addAction(a) + a.setShortcut(Qt.Key_Escape) + a.setEnabled(False) + a.triggered.connect(self.action_full_screen.trigger) self.print_menu = QMenu() self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) @@ -299,7 +328,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup) self.connect(self.action_print, SIGNAL("triggered(bool)"), partial(self.print_book, preview=False)) self.connect(self.print_menu.actions()[0], SIGNAL("triggered(bool)"), partial(self.print_book, preview=True)) - self.set_max_width() ca = self.view.copy_action ca.setShortcut(QKeySequence.Copy) self.addAction(ca) @@ -313,6 +341,13 @@ class EbookViewer(MainWindow, Ui_EbookViewer): w = self.tool_bar.widgetForAction(self.action_open_ebook) w.setPopupMode(QToolButton.MenuButtonPopup) + for x in ('tool_bar', 'tool_bar2'): + x = getattr(self, x) + for action in x.actions(): + # So that the keyboard shortcuts for these actions will + # continue to function even when the toolbars are hidden + self.addAction(action) + self.restore_state() def set_toc_visible(self, yes): @@ -338,9 +373,18 @@ class EbookViewer(MainWindow, Ui_EbookViewer): count += 1 def closeEvent(self, e): + if self.isFullScreen(): + self.action_full_screen.trigger() + e.ignore() + return self.save_state() return MainWindow.closeEvent(self, e) + def toggle_toolbars(self): + for x in ('tool_bar', 'tool_bar2'): + x = getattr(self, x) + x.setVisible(not x.isVisible()) + def save_state(self): state = bytearray(self.saveState(self.STATE_VERSION)) vprefs['viewer_toolbar_state'] = state @@ -382,11 +426,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self._lookup = None self.dictionary_view.setHtml(html) - def set_max_width(self): - from calibre.gui2.viewer.documentview import config - c = config().parse() - self.frame.setMaximumWidth(c.max_view_width) - def get_remember_current_page_opt(self): from calibre.gui2.viewer.documentview import config c = config().parse() @@ -401,6 +440,46 @@ class EbookViewer(MainWindow, Ui_EbookViewer): else: self.showFullScreen() + def showFullScreen(self): + self.tool_bar.setVisible(False) + self.tool_bar2.setVisible(False) + self._original_frame_margins = ( + self.centralwidget.layout().contentsMargins(), + self.frame.layout().contentsMargins()) + self.frame.layout().setContentsMargins(0, 0, 0, 0) + self.centralwidget.layout().setContentsMargins(0, 0, 0, 0) + + super(EbookViewer, self).showFullScreen() + QTimer.singleShot(10, self.show_full_screen_label) + + def show_full_screen_label(self): + f = self.full_screen_label + self.esc_full_screen_action.setEnabled(True) + f.setVisible(True) + height = 200 + width = int(0.7*self.view.width()) + f.resize(width, height) + f.move((self.view.width() - width)//2, (self.view.height()-height)//2) + a = self.full_screen_label_anim + a.setDuration(500) + a.setStartValue(QSize(width, 0)) + a.setEndValue(QSize(width, height)) + a.start() + QTimer.singleShot(2750, self.full_screen_label.hide) + self.view.document.switch_to_fullscreen_mode() + + def showNormal(self): + self.esc_full_screen_action.setEnabled(False) + self.tool_bar.setVisible(True) + self.tool_bar2.setVisible(True) + self.full_screen_label.setVisible(False) + if hasattr(self, '_original_frame_margins'): + om = self._original_frame_margins + self.centralwidget.layout().setContentsMargins(om[0]) + self.frame.layout().setContentsMargins(om[1]) + super(EbookViewer, self).showNormal() + self.view.document.switch_to_window_mode() + def goto(self, ref): if ref: tokens = ref.split('.') diff --git a/src/calibre/gui2/viewer/main.ui b/src/calibre/gui2/viewer/main.ui index 3137ad2e07..659a534fa8 100644 --- a/src/calibre/gui2/viewer/main.ui +++ b/src/calibre/gui2/viewer/main.ui @@ -284,6 +284,9 @@ Toggle full screen + + Toggle full screen (F11) +