Merge from trunk

This commit is contained in:
Charles Haley 2010-09-22 11:44:43 +01:00
commit 2c1debbe79
16 changed files with 395 additions and 124 deletions

View File

@ -49,7 +49,11 @@ class Danas(BasicNewsRecipe):
, 'language' : language , 'language' : language
} }
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] preprocess_regexps = [
(re.compile(u'\u0110'), lambda match: u'\u00D0')
,(re.compile(u'\u201c'), lambda match: '"')
,(re.compile(u'\u201e'), lambda match: '"')
]
keep_only_tags = [dict(name='div', attrs={'id':'left'})] keep_only_tags = [dict(name='div', attrs={'id':'left'})]
remove_tags = [ remove_tags = [

View File

@ -50,3 +50,5 @@ class Novosti(BasicNewsRecipe):
item['alt'] = 'image' item['alt'] = 'image'
return soup return soup

View File

@ -0,0 +1,79 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Luciano Furtado <lrfurtado at yahoo.com.br>'
'''
www.superesportes.com.br
'''
from calibre.web.feeds.news import BasicNewsRecipe
class SuperEsportesRecipe(BasicNewsRecipe):
title = u'www.superesportes.com.br'
description = u'Superesportes - Notícias do esporte no Brasil e no mundo'
__author__ = 'Luciano Furtado'
language = 'pt'
category = 'esportes, Brasil'
no_stylesheets = True
oldest_article = 7
use_embedded_content=0
max_articles_per_feed = 10
cover_url = 'http://imgs.mg.superesportes.com.br/superesportes_logo.png'
extra_css = 'div.info_noticias h1 { font-size: 100% }'
remove_tags = [
dict(name='div',attrs={'class':'topo'}),
dict(name='div',attrs={'class':'rodape'}),
dict(name='div',attrs={'class':'navegacao'}),
dict(name='div',attrs={'class':'lateral2'}),
dict(name='div',attrs={'class':'leia_mais'}),
dict(name='div',attrs={'id':'comentar'}),
dict(name='div',attrs={'id':'vrumelc_noticia'}),
dict(name='div',attrs={'class':'compartilhe'}),
dict(name='div',attrs={'class':'linha_noticias'}),
dict(name='div',attrs={'class':'botoes_noticias'}),
dict(name='div',attrs={'class':'barra_time bg_time'}),
]
def parse_index(self):
feeds = []
sections = [
(u'Atletico', 'http://www.df.superesportes.com.br/futebol/atletico-mg/capa_atletico_mg/index.shtml'),
(u'Botafogo', 'http://www.df.superesportes.com.br/futebol/botafogo/capa_botafogo/index.shtml'),
(u'Corinthinas', 'http://www.df.superesportes.com.br/futebol/corinthians/capa_corinthians/index.shtml'),
(u'Cruzeiro', 'http://www.df.superesportes.com.br/futebol/cruzeiro/capa_cruzeiro/index.shtml'),
(u'Flamengo', 'http://www.df.superesportes.com.br/futebol/flamengo/capa_flamengo/index.shtml'),
(u'Fluminense', 'http://www.df.superesportes.com.br/futebol/fluminense/capa_fluminense/index.shtml'),
(u'Palmeiras', 'http://www.df.superesportes.com.br/futebol/palmeiras/capa_palmeiras/index.shtml'),
(u'Santos', 'http://www.df.superesportes.com.br/futebol/santos/capa_santos/index.shtml'),
(u'S√£o Paulo', 'http://www.df.superesportes.com.br/futebol/sao-paulo/capa_sao_paulo/index.shtml'),
(u'Vasco', 'http://www.df.superesportes.com.br/futebol/vasco/capa_vasco/index.shtml'),
]
for section, url in sections:
current_articles = []
soup = self.index_to_soup(url)
latestNews = soup.find(name='ul',attrs={'class': 'lista_ultimas_noticias'})
for li_tag in latestNews.findAll(name='li'):
a_tag = li_tag.find('a', href= True)
if a_tag is None:
continue
title = self.tag_to_string(a_tag)
url = a_tag.get('href', False)
self.log("\n\nFound title: " + title + "\nUrl: " + url + "\nSection: " + section)
current_articles.append({'title': title, 'url': url, 'description': title, 'date':''})
if current_articles:
feeds.append((section, current_articles))
return feeds

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.7.900' __version__ = '0.7.901'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re import re

View File

@ -36,13 +36,17 @@ Run an embedded python interpreter.
'plugin code.') 'plugin code.')
parser.add_option('--reinitialize-db', default=None, parser.add_option('--reinitialize-db', default=None,
help='Re-initialize the sqlite calibre database at the ' help='Re-initialize the sqlite calibre database at the '
'specified path. Useful to recover from db corruption.') 'specified path. Useful to recover from db corruption.'
' You can also specify the path to an SQL dump which '
'will be used instead of trying to dump the database.'
' This can be useful when dumping fails, but dumping '
'with sqlite3 works.')
parser.add_option('-p', '--py-console', help='Run python console', parser.add_option('-p', '--py-console', help='Run python console',
default=False, action='store_true') default=False, action='store_true')
return parser return parser
def reinit_db(dbpath, callback=None): def reinit_db(dbpath, callback=None, sql_dump=None):
if not os.path.exists(dbpath): if not os.path.exists(dbpath):
raise ValueError(dbpath + ' does not exist') raise ValueError(dbpath + ' does not exist')
from calibre.library.sqlite import connect from calibre.library.sqlite import connect
@ -52,13 +56,17 @@ def reinit_db(dbpath, callback=None):
uv = conn.get('PRAGMA user_version;', all=False) uv = conn.get('PRAGMA user_version;', all=False)
conn.execute('PRAGMA writable_schema=ON') conn.execute('PRAGMA writable_schema=ON')
conn.commit() conn.commit()
if sql_dump is None:
sql_lines = conn.dump() sql_lines = conn.dump()
else:
sql_lines = open(sql_dump, 'rb').read()
conn.close() conn.close()
dest = dbpath + '.tmp' dest = dbpath + '.tmp'
try: try:
with closing(connect(dest, False)) as nconn: with closing(connect(dest, False)) as nconn:
nconn.execute('create temporary table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)') nconn.execute('create temporary table temp_sequence(id INTEGER PRIMARY KEY AUTOINCREMENT)')
nconn.commit() nconn.commit()
if sql_dump is None:
if callable(callback): if callable(callback):
callback(len(sql_lines), True) callback(len(sql_lines), True)
for i, line in enumerate(sql_lines): for i, line in enumerate(sql_lines):
@ -72,6 +80,8 @@ def reinit_db(dbpath, callback=None):
finally: finally:
if callable(callback): if callable(callback):
callback(i, False) callback(i, False)
else:
nconn.executescript(sql_lines)
nconn.execute('pragma user_version=%d'%int(uv)) nconn.execute('pragma user_version=%d'%int(uv))
nconn.commit() nconn.commit()
os.remove(dbpath) os.remove(dbpath)
@ -170,7 +180,10 @@ def main(args=sys.argv):
prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location) prints('CALIBRE_EXTENSIONS_PATH='+sys.extensions_location)
prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path)) prints('CALIBRE_PYTHON_PATH='+os.pathsep.join(sys.path))
elif opts.reinitialize_db is not None: elif opts.reinitialize_db is not None:
reinit_db(opts.reinitialize_db) sql_dump = None
if len(args) > 1 and os.access(args[-1], os.R_OK):
sql_dump = args[-1]
reinit_db(opts.reinitialize_db, sql_dump=sql_dump)
else: else:
from calibre import ipython from calibre import ipython
ipython() ipython()

View File

@ -29,7 +29,9 @@ class ANDROID(USBMS):
# Sony Ericsson # Sony Ericsson
0xfce : { 0xd12e : [0x0100]}, 0xfce : { 0xd12e : [0x0100]},
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]}, # Google
0x18d1 : { 0x4e11 : [0x0100, 0x226, 0x227], 0x4e12: [0x0100, 0x226,
0x227]},
# Samsung # Samsung
0x04e8 : { 0x681d : [0x0222, 0x0400], 0x04e8 : { 0x681d : [0x0222, 0x0400],

View File

@ -11,6 +11,10 @@ import re
from calibre.devices.usbms.driver import USBMS from calibre.devices.usbms.driver import USBMS
def is_alex(device_info):
return device_info[3] == u'Linux 2.6.28 with pxa3xx_u2d' and \
device_info[4] == u'Seleucia Disk'
class N516(USBMS): class N516(USBMS):
name = 'N516 driver' name = 'N516 driver'
@ -34,6 +38,9 @@ class N516(USBMS):
EBOOK_DIR_MAIN = 'e_book' EBOOK_DIR_MAIN = 'e_book'
SUPPORTS_SUB_DIRS = True SUPPORTS_SUB_DIRS = True
def can_handle(self, device_info, debug=False):
return not is_alex(device_info)
class THEBOOK(N516): class THEBOOK(N516):
name = 'The Book driver' name = 'The Book driver'
gui_name = 'The Book' gui_name = 'The Book'
@ -61,6 +68,9 @@ class ALEX(N516):
EBOOK_DIR_MAIN = 'eBooks' EBOOK_DIR_MAIN = 'eBooks'
SUPPORTS_SUB_DIRS = True SUPPORTS_SUB_DIRS = True
def can_handle(self, device_info, debug=False):
return is_alex(device_info)
class AZBOOKA(ALEX): class AZBOOKA(ALEX):
name = 'Azbooka driver' name = 'Azbooka driver'
@ -74,6 +84,9 @@ class AZBOOKA(ALEX):
EBOOK_DIR_MAIN = '' EBOOK_DIR_MAIN = ''
def can_handle(self, device_info, debug=False):
return not is_alex(device_info)
class EB511(USBMS): class EB511(USBMS):
name = 'Elonex EB 511 driver' name = 'Elonex EB 511 driver'

View File

@ -98,6 +98,8 @@ class KOBO(USBMS):
if readstatus == 1: if readstatus == 1:
playlist_map[lpath]= "Im_Reading" playlist_map[lpath]= "Im_Reading"
elif readstatus == 2:
playlist_map[lpath]= "Read"
path = self.normalize_path(path) path = self.normalize_path(path)
# print "Normalized FileName: " + path # print "Normalized FileName: " + path
@ -441,6 +443,11 @@ class KOBO(USBMS):
connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite') connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite')
cursor = connection.cursor() cursor = connection.cursor()
if collections:
# Process any collections that exist
for category, books in collections.items():
if category == 'Im_Reading':
# Reset Im_Reading list in the database # Reset Im_Reading list in the database
if oncard == 'carda': if oncard == 'carda':
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\'' query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\''
@ -456,8 +463,6 @@ class KOBO(USBMS):
# debug_print('Commit: Reset Im_Reading list') # debug_print('Commit: Reset Im_Reading list')
connection.commit() connection.commit()
for category, books in collections.items():
if category == 'Im_Reading':
for book in books: for book in books:
# debug_print('Title:', book.title, 'lpath:', book.path) # debug_print('Title:', book.title, 'lpath:', book.path)
book.device_collections = ['Im_Reading'] book.device_collections = ['Im_Reading']
@ -478,6 +483,59 @@ class KOBO(USBMS):
else: else:
connection.commit() connection.commit()
# debug_print('Database: Commit create Im_Reading list') # debug_print('Database: Commit create Im_Reading list')
if category == 'Read':
# Reset Im_Reading list in the database
if oncard == 'carda':
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID like \'file:///mnt/sd/%\''
elif oncard != 'carda' and oncard != 'cardb':
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID not like \'file:///mnt/sd/%\''
try:
cursor.execute (query)
except:
debug_print('Database Exception: Unable to reset Im_Reading list')
raise
else:
# debug_print('Commit: Reset Im_Reading list')
connection.commit()
for book in books:
# debug_print('Title:', book.title, 'lpath:', book.path)
book.device_collections = ['Read']
extension = os.path.splitext(book.path)[1]
ContentType = self.get_content_type_from_extension(extension)
ContentID = self.contentid_from_path(book.path, ContentType)
# datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
t = (ContentID,)
try:
cursor.execute('update content set ReadStatus=2,FirstTimeReading=\'true\' where BookID is Null and ContentID = ?', t)
except:
debug_print('Database Exception: Unable set book as Rinished')
raise
else:
connection.commit()
# debug_print('Database: Commit set ReadStatus as Finished')
else: # No collections
# Since no collections exist the ReadStatus needs to be reset to 0 (Unread)
print "Reseting ReadStatus to 0"
# Reset Im_Reading list in the database
if oncard == 'carda':
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID like \'file:///mnt/sd/%\''
elif oncard != 'carda' and oncard != 'cardb':
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID not like \'file:///mnt/sd/%\''
try:
cursor.execute (query)
except:
debug_print('Database Exception: Unable to reset Im_Reading list')
raise
else:
# debug_print('Commit: Reset Im_Reading list')
connection.commit()
cursor.close() cursor.close()
connection.close() connection.close()

View File

@ -1574,6 +1574,7 @@ class MobiWriter(object):
id = unicode(oeb.metadata.cover[0]) id = unicode(oeb.metadata.cover[0])
item = oeb.manifest.ids[id] item = oeb.manifest.ids[id]
href = item.href href = item.href
if href in self._images:
index = self._images[href] - 1 index = self._images[href] - 1
exth.write(pack('>III', 0xc9, 0x0c, index)) exth.write(pack('>III', 0xc9, 0x0c, index))
exth.write(pack('>III', 0xcb, 0x0c, 0)) exth.write(pack('>III', 0xcb, 0x0c, 0))

View File

@ -590,7 +590,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
mi.pubdate = self.pubdate(idx, index_is_id=index_is_id) mi.pubdate = self.pubdate(idx, index_is_id=index_is_id)
mi.uuid = self.uuid(idx, index_is_id=index_is_id) mi.uuid = self.uuid(idx, index_is_id=index_is_id)
mi.title_sort = self.title_sort(idx, index_is_id=index_is_id) mi.title_sort = self.title_sort(idx, index_is_id=index_is_id)
mi.formats = self.formats(idx, index_is_id=index_is_id).split(',') mi.formats = self.formats(idx, index_is_id=index_is_id)
if hasattr(mi.formats, 'split'):
mi.formats = mi.formats.split(',')
else:
mi.formats = None
tags = self.tags(idx, index_is_id=index_is_id) tags = self.tags(idx, index_is_id=index_is_id)
if tags: if tags:
mi.tags = [i.strip() for i in tags.split(',')] mi.tags = [i.strip() for i in tags.split(',')]
@ -1213,7 +1217,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def set_authors(self, id, authors, notify=True, commit=True): def set_authors(self, id, authors, notify=True, commit=True):
''' '''
`authors`: A list of authors. Note that even if commit is False, the db will still be committed to
because this causes the location of files to change
:param authors: A list of authors.
''' '''
if not authors: if not authors:
authors = [_('Unknown')] authors = [_('Unknown')]
@ -1250,6 +1257,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.notify('metadata', [id]) self.notify('metadata', [id])
def set_title(self, id, title, notify=True, commit=True): def set_title(self, id, title, notify=True, commit=True):
'''
Note that even if commit is False, the db will still be committed to
because this causes the location of files to change
'''
if not title: if not title:
return return
if not isinstance(title, unicode): if not isinstance(title, unicode):

View File

@ -60,15 +60,15 @@ def identify(path):
data = open(path, 'rb').read() data = open(path, 'rb').read()
return identify_data(data) return identify_data(data)
def add_borders_to_image(path_to_image, left=0, top=0, right=0, bottom=0, def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0,
border_color='#ffffff'): border_color='#ffffff', fmt='jpg'):
img = Image() img = Image()
img.open(path_to_image) img.load(img_data)
lwidth, lheight = img.size lwidth, lheight = img.size
canvas = create_canvas(lwidth+left+right, lheight+top+bottom, canvas = create_canvas(lwidth+left+right, lheight+top+bottom,
border_color) border_color)
canvas.compose(img, left, top) canvas.compose(img, left, top)
canvas.save(path_to_image) return canvas.export(fmt)
def create_text_wand(font_size, font_path=None): def create_text_wand(font_size, font_path=None):
if font_path is None: if font_path is None:

View File

@ -8,14 +8,18 @@ __docformat__ = 'restructuredtext en'
import sys import sys
from calibre import prints as prints_ from calibre import prints as prints_
from calibre.utils.config import Config, StringConfig from calibre.utils.config import Config, ConfigProxy
def console_config(defaults=None): def console_config():
desc=_('Settings to control the calibre content server') desc='Settings to control the calibre console'
c = Config('console', desc) if defaults is None else StringConfig(defaults, desc) c = Config('console', desc)
c.add_opt('--theme', default='default', help='The color theme') c.add_opt('theme', default='native', help='The color theme')
return c
prefs = ConfigProxy(console_config())
def prints(*args, **kwargs): def prints(*args, **kwargs):

View File

@ -6,16 +6,18 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys, textwrap, traceback, StringIO import sys, textwrap, traceback, StringIO
from functools import partial
from PyQt4.Qt import QTextEdit, Qt, QTextFrameFormat, pyqtSignal, \ from PyQt4.Qt import QTextEdit, Qt, QTextFrameFormat, pyqtSignal, \
QCoreApplication QApplication, QColor, QPalette, QMenu, QActionGroup
from pygments.lexers import PythonLexer, PythonTracebackLexer from pygments.lexers import PythonLexer, PythonTracebackLexer
from pygments.styles import get_all_styles
from calibre.constants import __appname__, __version__ from calibre.constants import __appname__, __version__
from calibre.utils.pyconsole.formatter import Formatter from calibre.utils.pyconsole.formatter import Formatter
from calibre.utils.pyconsole.repl import Interpreter, DummyFile from calibre.utils.pyconsole.repl import Interpreter, DummyFile
from calibre.utils.pyconsole import prints from calibre.utils.pyconsole import prints, prefs
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog
class EditBlock(object): # {{{ class EditBlock(object): # {{{
@ -47,6 +49,30 @@ class Prepender(object): # {{{
self.console.cursor_pos = self.opos self.console.cursor_pos = self.opos
# }}} # }}}
class ThemeMenu(QMenu): # {{{
def __init__(self, parent):
QMenu.__init__(self, _('Choose theme (needs restart)'))
parent.addMenu(self)
self.group = QActionGroup(self)
current = prefs['theme']
alls = list(sorted(get_all_styles()))
if current not in alls:
current = prefs['theme'] = 'default'
self.actions = []
for style in alls:
ac = self.group.addAction(style)
ac.setCheckable(True)
if current == style:
ac.setChecked(True)
self.actions.append(ac)
ac.triggered.connect(partial(self.set_theme, style))
self.addAction(ac)
def set_theme(self, style, *args):
prefs['theme'] = style
# }}}
class Console(QTextEdit): class Console(QTextEdit):
@ -99,8 +125,16 @@ class Console(QTextEdit):
self.doc.setMaximumBlockCount(10000) self.doc.setMaximumBlockCount(10000)
self.lexer = PythonLexer(ensurenl=False) self.lexer = PythonLexer(ensurenl=False)
self.tb_lexer = PythonTracebackLexer() self.tb_lexer = PythonTracebackLexer()
self.formatter = Formatter(prompt, continuation, style='default')
self.setStyleSheet(self.formatter.stylesheet) self.context_menu = cm = QMenu(self) # {{{
cm.theme = ThemeMenu(cm)
# }}}
self.formatter = Formatter(prompt, continuation, style=prefs['theme'])
p = QPalette()
p.setColor(p.Base, QColor(self.formatter.background_color))
p.setColor(p.Text, QColor(self.formatter.color))
self.setPalette(p)
self.key_dispatcher = { # {{{ self.key_dispatcher = { # {{{
Qt.Key_Enter : self.enter_pressed, Qt.Key_Enter : self.enter_pressed,
@ -109,6 +143,8 @@ class Console(QTextEdit):
Qt.Key_End : self.end_pressed, Qt.Key_End : self.end_pressed,
Qt.Key_Left : self.left_pressed, Qt.Key_Left : self.left_pressed,
Qt.Key_Right : self.right_pressed, Qt.Key_Right : self.right_pressed,
Qt.Key_Backspace : self.backspace_pressed,
Qt.Key_Delete : self.delete_pressed,
} # }}} } # }}}
motd = textwrap.dedent('''\ motd = textwrap.dedent('''\
@ -127,6 +163,9 @@ class Console(QTextEdit):
sys.excepthook = self.unhandled_exception sys.excepthook = self.unhandled_exception
def contextMenuEvent(self, event):
self.context_menu.popup(event.globalPos())
event.accept()
# Prompt management {{{ # Prompt management {{{
@ -239,7 +278,7 @@ class Console(QTextEdit):
except: except:
prints(tb, end='') prints(tb, end='')
self.ensureCursorVisible() self.ensureCursorVisible()
QCoreApplication.processEvents() QApplication.processEvents()
def show_output(self, raw): def show_output(self, raw):
def do_show(): def do_show():
@ -257,7 +296,7 @@ class Console(QTextEdit):
else: else:
do_show() do_show()
self.ensureCursorVisible() self.ensureCursorVisible()
QCoreApplication.processEvents() QApplication.processEvents()
# }}} # }}}
@ -290,6 +329,22 @@ class Console(QTextEdit):
self.setTextCursor(c) self.setTextCursor(c)
self.ensureCursorVisible() self.ensureCursorVisible()
def backspace_pressed(self):
lineno, pos = self.cursor_pos
if lineno < 0: return
if pos > self.prompt_len:
self.cursor.deletePreviousChar()
elif lineno > 0:
c = self.cursor
c.movePosition(c.Up)
c.movePosition(c.EndOfLine)
self.setTextCursor(c)
self.ensureCursorVisible()
def delete_pressed(self):
self.cursor.deleteChar()
self.ensureCursorVisible()
def right_pressed(self): def right_pressed(self):
lineno, pos = self.cursor_pos lineno, pos = self.cursor_pos
if lineno < 0: return if lineno < 0: return
@ -305,6 +360,11 @@ class Console(QTextEdit):
def home_pressed(self): def home_pressed(self):
if self.prompt_frame is not None: if self.prompt_frame is not None:
mods = QApplication.keyboardModifiers()
ctrl = bool(int(mods & Qt.CTRL))
if ctrl:
self.cursor_pos = (0, self.prompt_len)
else:
c = self.cursor c = self.cursor
c.movePosition(c.StartOfLine) c.movePosition(c.StartOfLine)
c.movePosition(c.NextCharacter, n=self.prompt_len) c.movePosition(c.NextCharacter, n=self.prompt_len)
@ -313,6 +373,10 @@ class Console(QTextEdit):
def end_pressed(self): def end_pressed(self):
if self.prompt_frame is not None: if self.prompt_frame is not None:
mods = QApplication.keyboardModifiers()
ctrl = bool(int(mods & Qt.CTRL))
if ctrl:
self.cursor_pos = (len(list(self.prompt()))-1, self.prompt_len)
c = self.cursor c = self.cursor
c.movePosition(c.EndOfLine) c.movePosition(c.EndOfLine)
self.setTextCursor(c) self.setTextCursor(c)

View File

@ -8,18 +8,20 @@ __docformat__ = 'restructuredtext en'
from PyQt4.Qt import QTextCharFormat, QFont, QBrush, QColor from PyQt4.Qt import QTextCharFormat, QFont, QBrush, QColor
from pygments.formatter import Formatter as PF from pygments.formatter import Formatter as PF
from pygments.token import Token, Generic from pygments.token import Token, Generic, string_to_tokentype
class Formatter(object): class Formatter(object):
def __init__(self, prompt, continuation, **options): def __init__(self, prompt, continuation, style='default'):
if len(prompt) != len(continuation): if len(prompt) != len(continuation):
raise ValueError('%r does not have the same length as %r' % raise ValueError('%r does not have the same length as %r' %
(prompt, continuation)) (prompt, continuation))
self.prompt, self.continuation = prompt, continuation self.prompt, self.continuation = prompt, continuation
self.set_style(style)
pf = PF(**options) def set_style(self, style):
pf = PF(style=style)
self.styles = {} self.styles = {}
self.normal = self.base_fmt() self.normal = self.base_fmt()
self.background_color = pf.style.background_color self.background_color = pf.style.background_color
@ -27,6 +29,7 @@ class Formatter(object):
for ttype, ndef in pf.style: for ttype, ndef in pf.style:
fmt = self.base_fmt() fmt = self.base_fmt()
fmt.setProperty(fmt.UserProperty, str(ttype))
if ndef['color']: if ndef['color']:
fmt.setForeground(QBrush(QColor('#%s'%ndef['color']))) fmt.setForeground(QBrush(QColor('#%s'%ndef['color'])))
fmt.setUnderlineColor(QColor('#%s'%ndef['color'])) fmt.setUnderlineColor(QColor('#%s'%ndef['color']))
@ -45,10 +48,14 @@ class Formatter(object):
self.styles[ttype] = fmt self.styles[ttype] = fmt
self.stylesheet = ''' def get_fmt(self, token):
QTextEdit { color: %s; background-color: %s } if type(token) != type(Token.Generic):
'''%(self.color, self.background_color) token = string_to_tokentype(token)
fmt = self.styles.get(token, None)
if fmt is None:
fmt = self.base_fmt()
fmt.setProperty(fmt.UserProperty, str(token))
return fmt
def base_fmt(self): def base_fmt(self):
fmt = QTextCharFormat() fmt = QTextCharFormat()
@ -59,7 +66,7 @@ class Formatter(object):
cursor.insertText(raw, self.normal) cursor.insertText(raw, self.normal)
def render_syntax_error(self, tb, cursor): def render_syntax_error(self, tb, cursor):
fmt = self.styles[Token.Error] fmt = self.get_fmt(Token.Error)
cursor.insertText(tb, fmt) cursor.insertText(tb, fmt)
def render(self, tokens, cursor): def render(self, tokens, cursor):
@ -84,7 +91,9 @@ class Formatter(object):
def render_prompt(self, is_continuation, cursor): def render_prompt(self, is_continuation, cursor):
pr = self.continuation if is_continuation else self.prompt pr = self.continuation if is_continuation else self.prompt
fmt = self.styles[Generic.Prompt] fmt = self.get_fmt(Generic.Prompt)
if fmt is None:
fmt = self.base_fmt()
cursor.insertText(pr, fmt) cursor.insertText(pr, fmt)

View File

@ -9,7 +9,7 @@ __version__ = '0.1.0'
from functools import partial from functools import partial
from PyQt4.Qt import QDialog, QToolBar, QStatusBar, QLabel, QFont, Qt, \ from PyQt4.Qt import QDialog, QToolBar, QStatusBar, QLabel, QFont, Qt, \
QApplication, QIcon, QVBoxLayout QApplication, QIcon, QVBoxLayout, QAction
from calibre.constants import __appname__, __version__ from calibre.constants import __appname__, __version__
from calibre.utils.pyconsole.console import Console from calibre.utils.pyconsole.console import Console
@ -19,8 +19,9 @@ class MainWindow(QDialog):
def __init__(self, def __init__(self,
default_status_msg=_('Welcome to') + ' ' + __appname__+' console', default_status_msg=_('Welcome to') + ' ' + __appname__+' console',
parent=None): parent=None):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
self.restart_requested = False
self.l = QVBoxLayout() self.l = QVBoxLayout()
self.setLayout(self.l) self.setLayout(self.l)
@ -51,14 +52,26 @@ class MainWindow(QDialog):
self.setWindowTitle(__appname__ + ' console') self.setWindowTitle(__appname__ + ' console')
self.setWindowIcon(QIcon(I('console.png'))) self.setWindowIcon(QIcon(I('console.png')))
self.restart_action = QAction(_('Restart console'), self)
self.restart_action.setShortcut(_('Ctrl+R'))
self.addAction(self.restart_action)
self.restart_action.triggered.connect(self.restart)
self.console.context_menu.addAction(self.restart_action)
def restart(self):
self.restart_requested = True
self.reject()
def main(): def main():
QApplication.setApplicationName(__appname__+' console') QApplication.setApplicationName(__appname__+' console')
QApplication.setOrganizationName('Kovid Goyal') QApplication.setOrganizationName('Kovid Goyal')
app = QApplication([]) app = QApplication([])
app
while True:
m = MainWindow() m = MainWindow()
m.show() m.exec_()
app.exec_() if not m.restart_requested:
break
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -7,7 +7,7 @@ Defines various abstract base classes that can be subclassed to create powerful
__docformat__ = "restructuredtext en" __docformat__ = "restructuredtext en"
import os, time, traceback, re, urlparse, sys import os, time, traceback, re, urlparse, sys, cStringIO
from collections import defaultdict from collections import defaultdict
from functools import partial from functools import partial
from contextlib import nested, closing from contextlib import nested, closing
@ -27,6 +27,7 @@ from calibre.web.fetch.simple import RecursiveFetcher
from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending from calibre.utils.threadpool import WorkRequest, ThreadPool, NoResultsPending
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.date import now as nowf from calibre.utils.date import now as nowf
from calibre.utils.magick.draw import save_cover_data_to, add_borders_to_image
class LoginFailed(ValueError): class LoginFailed(ValueError):
pass pass
@ -948,38 +949,36 @@ class BasicNewsRecipe(Recipe):
try: try:
cu = self.get_cover_url() cu = self.get_cover_url()
except Exception, err: except Exception, err:
cu = None
self.log.error(_('Could not download cover: %s')%str(err)) self.log.error(_('Could not download cover: %s')%str(err))
self.log.debug(traceback.format_exc()) self.log.debug(traceback.format_exc())
if cu is not None: else:
ext = cu.split('/')[-1].rpartition('.')[-1] cdata = None
if '?' in ext:
ext = ''
ext = ext.lower() if ext and '/' not in ext else 'jpg'
cpath = os.path.join(self.output_dir, 'cover.'+ext)
if os.access(cu, os.R_OK): if os.access(cu, os.R_OK):
with open(cpath, 'wb') as cfile: cdata = open(cu, 'rb').read()
cfile.write(open(cu, 'rb').read())
else: else:
self.report_progress(1, _('Downloading cover from %s')%cu) self.report_progress(1, _('Downloading cover from %s')%cu)
with nested(open(cpath, 'wb'), closing(self.browser.open(cu))) as (cfile, r): with closing(self.browser.open(cu)) as r:
cfile.write(r.read()) cdata = r.read()
if not cdata:
return
ext = cu.split('/')[-1].rpartition('.')[-1].lower().strip()
if ext == 'pdf':
from calibre.ebooks.metadata.pdf import get_metadata
stream = cStringIO.StringIO(cdata)
cdata = None
mi = get_metadata(stream)
if mi.cover_data and mi.cover_data[1]:
cdata = mi.cover_data[1]
if not cdata:
return
if self.cover_margins[0] or self.cover_margins[1]: if self.cover_margins[0] or self.cover_margins[1]:
from calibre.utils.magick.draw import add_borders_to_image cdata = add_borders_to_image(cdata,
add_borders_to_image(cpath,
left=self.cover_margins[0],right=self.cover_margins[0], left=self.cover_margins[0],right=self.cover_margins[0],
top=self.cover_margins[1],bottom=self.cover_margins[1], top=self.cover_margins[1],bottom=self.cover_margins[1],
border_color=self.cover_margins[2]) border_color=self.cover_margins[2])
if ext.lower() == 'pdf':
from calibre.ebooks.metadata.pdf import get_metadata cpath = os.path.join(self.output_dir, 'cover.jpg')
stream = open(cpath, 'rb') save_cover_data_to(cdata, cpath)
mi = get_metadata(stream)
cpath = None
if mi.cover_data and mi.cover_data[1]:
cpath = os.path.join(self.output_dir,
'cover.'+mi.cover_data[0])
with open(cpath, 'wb') as f:
f.write(mi.cover_data[1])
self.cover_path = cpath self.cover_path = cpath
def download_cover(self): def download_cover(self):
@ -1422,7 +1421,6 @@ class CalibrePeriodical(BasicNewsRecipe):
return br return br
def download(self): def download(self):
import cStringIO
self.log('Fetching downloaded recipe') self.log('Fetching downloaded recipe')
try: try:
raw = self.browser.open_novisit( raw = self.browser.open_novisit(