diff --git a/imgsrc/edit_copy.svg b/imgsrc/edit-copy.svg similarity index 100% rename from imgsrc/edit_copy.svg rename to imgsrc/edit-copy.svg diff --git a/imgsrc/edit-cut.svg b/imgsrc/edit-cut.svg new file mode 100644 index 0000000000..f078b52e04 --- /dev/null +++ b/imgsrc/edit-cut.svg @@ -0,0 +1,831 @@ + + + diff --git a/imgsrc/edit-paste.svg b/imgsrc/edit-paste.svg new file mode 100644 index 0000000000..d22a8bb7dd --- /dev/null +++ b/imgsrc/edit-paste.svg @@ -0,0 +1,3302 @@ + + + \ No newline at end of file diff --git a/imgsrc/swap.svg b/imgsrc/swap.svg deleted file mode 100644 index aa62316b34..0000000000 --- a/imgsrc/swap.svg +++ /dev/null @@ -1,722 +0,0 @@ - - - diff --git a/resources/images/edit_copy.png b/resources/images/edit-copy.png similarity index 100% rename from resources/images/edit_copy.png rename to resources/images/edit-copy.png diff --git a/resources/images/edit-cut.png b/resources/images/edit-cut.png new file mode 100644 index 0000000000..b995283754 Binary files /dev/null and b/resources/images/edit-cut.png differ diff --git a/resources/images/edit-paste.png b/resources/images/edit-paste.png new file mode 100644 index 0000000000..b790efec25 Binary files /dev/null and b/resources/images/edit-paste.png differ diff --git a/resources/images/edit-redo.png b/resources/images/edit-redo.png new file mode 100644 index 0000000000..8de333fe8c Binary files /dev/null and b/resources/images/edit-redo.png differ diff --git a/resources/images/edit-select-all.png b/resources/images/edit-select-all.png new file mode 100644 index 0000000000..4393bc9dc7 Binary files /dev/null and b/resources/images/edit-select-all.png differ diff --git a/resources/images/edit-undo.png b/resources/images/edit-undo.png new file mode 100644 index 0000000000..f6d7e8ba56 Binary files /dev/null and b/resources/images/edit-undo.png differ diff --git a/resources/images/format-indent-less.png b/resources/images/format-indent-less.png new file mode 100644 index 0000000000..8662c34871 Binary files /dev/null and b/resources/images/format-indent-less.png differ diff --git a/resources/images/format-indent-more.png b/resources/images/format-indent-more.png new file mode 100644 index 0000000000..e1244ef47c Binary files /dev/null and b/resources/images/format-indent-more.png differ diff --git a/resources/images/format-justify-center.png b/resources/images/format-justify-center.png new file mode 100644 index 0000000000..505160a1bb Binary files /dev/null and b/resources/images/format-justify-center.png differ diff --git a/resources/images/format-justify-fill.png b/resources/images/format-justify-fill.png new file mode 100644 index 0000000000..ee34b8272f Binary files /dev/null and b/resources/images/format-justify-fill.png differ diff --git a/resources/images/format-justify-left.png b/resources/images/format-justify-left.png new file mode 100644 index 0000000000..f5af823a82 Binary files /dev/null and b/resources/images/format-justify-left.png differ diff --git a/resources/images/format-justify-right.png b/resources/images/format-justify-right.png new file mode 100644 index 0000000000..9a3d8d6ee1 Binary files /dev/null and b/resources/images/format-justify-right.png differ diff --git a/resources/images/format-list-ordered.png b/resources/images/format-list-ordered.png new file mode 100644 index 0000000000..c7da85da3f Binary files /dev/null and b/resources/images/format-list-ordered.png differ diff --git a/resources/images/format-list-unordered.png b/resources/images/format-list-unordered.png new file mode 100644 index 0000000000..c959989958 Binary files /dev/null and b/resources/images/format-list-unordered.png differ diff --git a/resources/images/format-text-subscript.png b/resources/images/format-text-subscript.png new file mode 100644 index 0000000000..7da8882aea Binary files /dev/null and b/resources/images/format-text-subscript.png differ diff --git a/resources/images/format-text-superscript.png b/resources/images/format-text-superscript.png new file mode 100644 index 0000000000..9dc31ab783 Binary files /dev/null and b/resources/images/format-text-superscript.png differ diff --git a/resources/images/swap.png b/resources/images/swap.png index e5aeb60e22..7f8d40ca1d 100644 Binary files a/resources/images/swap.png and b/resources/images/swap.png differ diff --git a/resources/recipes/lanacion.recipe b/resources/recipes/lanacion.recipe index 19f6c1c897..050cb2e79c 100644 --- a/resources/recipes/lanacion.recipe +++ b/resources/recipes/lanacion.recipe @@ -78,4 +78,6 @@ class Lanacion(BasicNewsRecipe): ] def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] return self.adeify_images(soup) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index f2a5752bd4..492b00617d 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -24,7 +24,7 @@ class ANDROID(USBMS): 0xc92 : [0x100], 0xc97: [0x226]}, # Eken - 0x040d : { 0x8510 : [0x0001] }, + 0x040d : { 0x8510 : [0x0001], 0x0851 : [0x1] }, # Motorola 0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216], @@ -49,8 +49,9 @@ class ANDROID(USBMS): # Dell 0x413c : { 0xb007 : [0x0100, 0x0224]}, - # Eken? - 0x040d : { 0x0851 : [0x0001]}, + # LG + 0x1004 : { 0x61cc : [0x100] }, + } EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books'] EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to ' @@ -59,13 +60,13 @@ class ANDROID(USBMS): EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(EBOOK_DIR_MAIN) VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER', - 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX'] + 'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX', 'GOOGLE'] WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE', '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897', 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', - 'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810'] + 'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', - 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID'] + 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD'] OSX_MAIN_MEM = 'HTC Android Phone Media' diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index bc8b87533c..246b753fa8 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -266,3 +266,12 @@ class POCKETBOOK701(USBMS): VENDOR_NAME = 'ANDROID' WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE' + def windows_sort_drives(self, drives): + if len(drives) < 2: return drives + main = drives.get('main', None) + carda = drives.get('carda', None) + if main and carda: + drives['main'] = carda + drives['carda'] = main + return drives + diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index 68f4dba9a9..52952356f8 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -104,7 +104,7 @@ class PDNOVEL(USBMS): VENDOR_NAME = 'ANDROID' WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE' - THUMBNAIL_HEIGHT = 144 + THUMBNAIL_HEIGHT = 130 EBOOK_DIR_MAIN = 'eBooks' SUPPORTS_SUB_DIRS = False diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 10180541a1..6820709b3e 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -257,7 +257,6 @@ class EbookIterator(object): s.max_page = s.start_page + s.pages - 1 self.toc = self.opf.toc - self.find_embedded_fonts() self.read_bookmarks() return self diff --git a/src/calibre/gui2/actions/delete.py b/src/calibre/gui2/actions/delete.py index a0f49a7e9a..24dd1d3e5c 100644 --- a/src/calibre/gui2/actions/delete.py +++ b/src/calibre/gui2/actions/delete.py @@ -12,6 +12,7 @@ from PyQt4.Qt import QMenu, QObject, QTimer from calibre.gui2 import error_dialog from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog from calibre.gui2.dialogs.confirm_delete import confirm +from calibre.gui2.dialogs.confirm_delete_location import confirm_location from calibre.gui2.actions import InterfaceAction single_shot = partial(QTimer.singleShot, 10) @@ -223,7 +224,31 @@ class DeleteAction(InterfaceAction): rows = view.selectionModel().selectedRows() if not rows or len(rows) == 0: return + # Library view is visible. if self.gui.stack.currentIndex() == 0: + # Ask the user if they want to delete the book from the library or device if it is in both. + if self.gui.device_manager.is_device_connected: + on_device = False + on_device_ids = self._get_selected_ids() + for id in on_device_ids: + res = self.gui.book_on_device(id) + if res[0] or res[1] or res[2]: + on_device = True + if on_device: + break + if on_device: + loc = confirm_location('
' + _('Some of the selected books are on the attached device. ' + 'Where do you want the selected files deleted from?'), + self.gui) + if not loc: + return + elif loc == 'dev': + self.remove_matching_books_from_device() + return + elif loc == 'both': + self.remove_matching_books_from_device() + # The following will run if the selected books are not on a connected device. + # The user has selected to delete from the library or the device and library. if not confirm('
'+_('The selected books will be ' 'permanently deleted and the files ' 'removed from your calibre library. Are you sure?') @@ -239,7 +264,7 @@ class DeleteAction(InterfaceAction): else: self.__md = MultiDeleter(self.gui, rows, partial(self.library_ids_deleted, current_row=row)) - + # Device view is visible. else: if not confirm('
'+_('The selected books will be ' 'permanently deleted ' diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py index 8438fa3cfc..547dd51848 100644 --- a/src/calibre/gui2/comments_editor.py +++ b/src/calibre/gui2/comments_editor.py @@ -9,26 +9,85 @@ __docformat__ = 'restructuredtext en' from lxml import html from lxml.html import soupparser -from PyQt4.Qt import QApplication, QFontInfo, QPalette, QSize, QWidget, \ - QToolBar, QVBoxLayout, QAction, QIcon +from PyQt4.Qt import QApplication, QFontInfo, QSize, QWidget, QPlainTextEdit, \ + QToolBar, QVBoxLayout, QAction, QIcon, QWebPage, Qt, QTabWidget, \ + QSyntaxHighlighter, QColor, QChar from PyQt4.QtWebKit import QWebView from calibre.ebooks.chardet import xml_to_unicode from calibre import xml_replace_entities -class EditorWidget(QWebView): + +class PageAction(QAction): # {{{ + + def __init__(self, wac, icon, text, checkable, view): + QAction.__init__(self, QIcon(I(icon+'.png')), text, view) + self._page_action = getattr(QWebPage, wac) + self.setCheckable(checkable) + self.triggered.connect(self.trigger_page_action) + view.selectionChanged.connect(self.update_state, + type=Qt.QueuedConnection) + self.page_action.changed.connect(self.update_state, + type=Qt.QueuedConnection) + + @property + def page_action(self): + return self.parent().pageAction(self._page_action) + + def trigger_page_action(self, *args): + self.page_action.trigger() + + def update_state(self, *args): + if self.isCheckable(): + self.setChecked(self.page_action.isChecked()) + self.setEnabled(self.page_action.isEnabled()) + +# }}} + +class EditorWidget(QWebView): # {{{ def __init__(self, parent=None): QWebView.__init__(self, parent) - for name, icon, text, checkable in [ - ('bold', 'format-text-bold', _('Bold'), True), - ('italic', 'format-text-italic', _('Italic'), True), - ('underline', 'format-text-underline', _('Underline'), True), - ('strikethrough', 'format-text-underline', _('Underline'), True), + for wac, name, icon, text, checkable in [ + ('ToggleBold', 'bold', 'format-text-bold', _('Bold'), True), + ('ToggleItalic', 'italic', 'format-text-italic', _('Italic'), + True), + ('ToggleUnderline', 'underline', 'format-text-underline', + _('Underline'), True), + ('ToggleStrikethrough', 'strikethrough', 'format-text-strikethrough', + _('Strikethrough'), True), + ('ToggleSuperscript', 'superscript', 'format-text-superscript', + _('Superscript'), True), + ('ToggleSubscript', 'subscript', 'format-text-subscript', + _('Subscript'), True), + ('InsertOrderedList', 'ordered_list', 'format-list-ordered', + _('Ordered list'), True), + ('InsertUnorderedList', 'unordered_list', 'format-list-unordered', + _('Unordered list'), True), + + ('AlignLeft', 'align_left', 'format-justify-left', + _('Align left'), False), + ('AlignCenter', 'align_center', 'format-justify-center', + _('Align center'), False), + ('AlignRight', 'align_right', 'format-justify-right', + _('Align right'), False), + ('AlignJustified', 'align_justified', 'format-justify-fill', + _('Align justified'), False), + ('Undo', 'undo', 'edit-undo', _('Undo'), False), + ('Redo', 'redo', 'edit-redo', _('Redo'), False), + ('RemoveFormat', 'remove_format', 'trash', _('Remove formatting'), False), + ('Copy', 'copy', 'edit-copy', _('Copy'), False), + ('Paste', 'paste', 'edit-paste', _('Paste'), False), + ('Cut', 'cut', 'edit-cut', _('Cut'), False), + ('Indent', 'indent', 'format-indent-more', + _('Increase Indentation'), False), + ('Outdent', 'outdent', 'format-indent-less', + _('Decrease Indentation'), False), + ('SelectAll', 'select_all', 'edit-select-all', + _('Select all'), False), ]: - ac = QAction(QIcon(I(icon+'.png')), text, self) - ac.setCheckable(checkable) + ac = PageAction(wac, icon, text, checkable, self) setattr(self, 'action_'+name, ac) def sizeHint(self): @@ -67,12 +126,7 @@ class EditorWidget(QWebView): def fset(self, val): self.setHtml(val) f = QFontInfo(QApplication.font(self)).pixelSize() - b = unicode(QApplication.palette().color(QPalette.Normal, - QPalette.Base).name()) - c = unicode(QApplication.palette().color(QPalette.Normal, - QPalette.Text).name()) - style = 'font-size: %dpx; background-color: %s; color: %s' % (f, b, - c) + style = 'font-size: %dpx;' % (f,) for body in self.page().mainFrame().documentElement().findAll('body'): body.setAttribute('style', style) @@ -80,18 +134,278 @@ class EditorWidget(QWebView): return property(fget=fget, fset=fset) +# }}} + +# Highlighter {{{ +State_Text = -1 +State_DocType = 0 +State_Comment = 1 +State_TagStart = 2 +State_TagName = 3 +State_InsideTag = 4 +State_AttributeName = 5 +State_SingleQuote = 6 +State_DoubleQuote = 7 +State_AttributeValue = 8 + +class Highlighter(QSyntaxHighlighter): + + def __init__(self, doc): + QSyntaxHighlighter.__init__(self, doc) + self.colors = {} + self.colors['doctype'] = QColor(192, 192, 192) + self.colors['entity'] = QColor(128, 128, 128) + self.colors['tag'] = QColor(136, 18, 128) + self.colors['comment'] = QColor( 35, 110, 37) + self.colors['attrname'] = QColor(153, 69, 0) + self.colors['attrval'] = QColor( 36, 36, 170) + + def highlightBlock(self, text): + state = self.previousBlockState() + len_ = text.length() + start = 0 + pos = 0 + + while pos < len_: + + if state == State_Comment: + start = pos + while pos < len_: + if text.mid(pos, 3) == "-->": + pos += 3; + state = State_Text; + break + else: + pos += 1 + self.setFormat(start, pos - start, self.colors['comment']) + + elif state == State_DocType: + start = pos + while pos < len_: + ch = text.at(pos) + pos += 1 + if ch == QChar('>'): + state = State_Text + break + self.setFormat(start, pos - start, self.colors['doctype']) + + # at '<' in e.g. "foo" + elif state == State_TagStart: + start = pos + 1 + while pos < len_: + ch = text.at(pos) + pos += 1 + if ch == QChar('>'): + state = State_Text + break + if not ch.isSpace(): + pos -= 1 + state = State_TagName + break + + # at 'b' in e.g "
foo" + elif state == State_TagName: + start = pos + while pos < len_: + ch = text.at(pos) + pos += 1 + if ch.isSpace(): + pos -= 1 + state = State_InsideTag + break + if ch == QChar('>'): + state = State_Text + break + self.setFormat(start, pos - start, self.colors['tag']); + + # anywhere after tag name and before tag closing ('>') + elif state == State_InsideTag: + start = pos + + while pos < len_: + ch = text.at(pos) + pos += 1 + + if ch == QChar('/'): + continue + + if ch == QChar('>'): + state = State_Text + break + + if not ch.isSpace(): + pos -= 1 + state = State_AttributeName + break + + # at 's' in e.g.