diff --git a/recipes/f_secure.recipe b/recipes/f_secure.recipe index f276a4961a..5a03f01ac8 100644 --- a/recipes/f_secure.recipe +++ b/recipes/f_secure.recipe @@ -12,7 +12,6 @@ class AdvancedUserRecipe1301860159(BasicNewsRecipe): max_articles_per_feed = 100 no_stylesheets = True use_embedded_content = False - language = 'en_EN' remove_javascript = True keep_only_tags = [dict(name='div', attrs={'class':'modSectionTd2'})] remove_tags = [dict(name='a'),dict(name='hr')] diff --git a/recipes/frazpc.recipe b/recipes/frazpc.recipe index 56e45076ac..2d0d54d10c 100644 --- a/recipes/frazpc.recipe +++ b/recipes/frazpc.recipe @@ -1,7 +1,7 @@ #!/usr/bin/env python __license__ = 'GPL v3' -__copyright__ = u'2010, Tomasz Dlugosz ' +__copyright__ = u'2010-2011, Tomasz Dlugosz ' ''' frazpc.pl ''' @@ -19,17 +19,20 @@ class FrazPC(BasicNewsRecipe): use_embedded_content = False no_stylesheets = True - feeds = [(u'Aktualno\u015bci', u'http://www.frazpc.pl/feed'), (u'Recenzje', u'http://www.frazpc.pl/kat/recenzje-2/feed') ] - - keep_only_tags = [dict(name='div', attrs={'id':'FRAZ_CONTENT'})] - - remove_tags = [dict(name='p', attrs={'class':'gray tagsP fs11'})] - - preprocess_regexps = [ - (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in - [(r'
(Skomentuj|Komentarz(e)?\([0-9]*\))  \|', lambda match: '')] + feeds = [ + (u'Aktualno\u015bci', u'http://www.frazpc.pl/feed/aktualnosci'), + (u'Artyku\u0142y', u'http://www.frazpc.pl/feed/artykuly') ] + keep_only_tags = [dict(name='div', attrs={'class':'article'})] + + remove_tags = [ + dict(name='div', attrs={'class':'title-wrapper'}), + dict(name='p', attrs={'class':'tags'}), + dict(name='p', attrs={'class':'article-links'}), + dict(name='div', attrs={'class':'comments_box'}) + ] + + preprocess_regexps = [(re.compile(r'\| Komentarze \([0-9]*\)'), lambda match: '')] + remove_attributes = [ 'width', 'height' ] diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index c0996442ba..47d248de07 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -607,6 +607,7 @@ class StoreBase(Plugin): # {{{ supported_platforms = ['windows', 'osx', 'linux'] author = 'John Schember' type = _('Store') + minimum_calibre_version = (0, 7, 59) actual_plugin = None diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 776b04d5f6..c1da8391e0 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -613,6 +613,7 @@ from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, \ from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG from calibre.devices.kobo.driver import KOBO from calibre.devices.bambook.driver import BAMBOOK +from calibre.devices.boeye.driver import BOEYE_BEX, BOEYE_BDX from calibre.library.catalog import CSV_XML, EPUB_MOBI, BIBTEX from calibre.ebooks.epub.fix.unmanifested import Unmanifested @@ -743,6 +744,8 @@ plugins += [ EEEREADER, NEXTBOOK, ITUNES, + BOEYE_BEX, + BOEYE_BDX, USER_DEFINED, ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ diff --git a/src/calibre/devices/boeye/__init__.py b/src/calibre/devices/boeye/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/calibre/devices/boeye/driver.py b/src/calibre/devices/boeye/driver.py new file mode 100644 index 0000000000..5f82b68417 --- /dev/null +++ b/src/calibre/devices/boeye/driver.py @@ -0,0 +1,56 @@ +__license__ = 'GPL v3' +__copyright__ = '2011, Ken ' +__docformat__ = 'restructuredtext en' + +''' +Device driver for BOEYE serial readers +''' + +from calibre.devices.usbms.driver import USBMS + +class BOEYE_BEX(USBMS): + name = 'BOEYE BEX reader driver' + gui_name = 'BOEYE BEX' + description = _('Communicate with BOEYE BEX Serial eBook readers.') + author = 'szboeye' + supported_platforms = ['windows', 'osx', 'linux'] + + FORMATS = ['epub', 'mobi', 'fb2', 'lit', 'prc', 'pdf', 'rtf', 'txt', 'djvu', 'doc', 'chm', 'html', 'zip', 'pdb'] + + VENDOR_ID = [0x0085] + PRODUCT_ID = [0x600] + + VENDOR_NAME = 'LINUX' + WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET' + OSX_MAIN_MEM = 'Linux File-Stor Gadget Media' + + MAIN_MEMORY_VOLUME_LABEL = 'BOEYE BEX Storage Card' + + EBOOK_DIR_MAIN = 'Documents' + SUPPORTS_SUB_DIRS = True + +class BOEYE_BDX(USBMS): + name = 'BOEYE BDX reader driver' + gui_name = 'BOEYE BDX' + description = _('Communicate with BOEYE BDX serial eBook readers.') + author = 'szboeye' + supported_platforms = ['windows', 'osx', 'linux'] + + FORMATS = ['epub', 'mobi', 'fb2', 'lit', 'prc', 'pdf', 'rtf', 'txt', 'djvu', 'doc', 'chm', 'html', 'zip', 'pdb'] + + VENDOR_ID = [0x0085] + PRODUCT_ID = [0x800] + + VENDOR_NAME = 'LINUX' + WINDOWS_MAIN_MEM = 'FILE-STOR_GADGET' + WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET' + + OSX_MAIN_MEM = 'Linux File-Stor Gadget Media' + OSX_CARD_A_MEM = 'Linux File-Stor Gadget Media' + + MAIN_MEMORY_VOLUME_LABEL = 'BOEYE BDX Internal Memory' + STORAGE_CARD_VOLUME_LABEL = 'BOEYE BDX Storage Card' + + EBOOK_DIR_MAIN = 'Documents' + EBOOK_DIR_CARD_A = 'Documents' + SUPPORTS_SUB_DIRS = True diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 60d2a0a7dd..1dfe1d8d14 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -34,7 +34,7 @@ if isosx: ) gprefs.defaults['action-layout-toolbar'] = ( 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None, - 'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk', + 'Choose Library', 'Donate', None, 'Fetch News', 'Store', 'Save To Disk', 'Connect Share', None, 'Remove Books', ) gprefs.defaults['action-layout-toolbar-device'] = ( @@ -48,7 +48,7 @@ else: gprefs.defaults['action-layout-menubar-device'] = () gprefs.defaults['action-layout-toolbar'] = ( 'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None, - 'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk', + 'Choose Library', 'Donate', None, 'Fetch News', 'Store', 'Save To Disk', 'Connect Share', None, 'Remove Books', None, 'Help', 'Preferences', ) gprefs.defaults['action-layout-toolbar-device'] = ( diff --git a/src/calibre/gui2/actions/preferences.py b/src/calibre/gui2/actions/preferences.py index 24d20b23f9..b65967e994 100644 --- a/src/calibre/gui2/actions/preferences.py +++ b/src/calibre/gui2/actions/preferences.py @@ -10,7 +10,7 @@ from PyQt4.Qt import QIcon, QMenu, Qt from calibre.gui2.actions import InterfaceAction from calibre.gui2.preferences.main import Preferences from calibre.gui2 import error_dialog -from calibre.constants import DEBUG +from calibre.constants import DEBUG, isosx class PreferencesAction(InterfaceAction): @@ -19,7 +19,8 @@ class PreferencesAction(InterfaceAction): def genesis(self): pm = QMenu() - pm.addAction(QIcon(I('config.png')), _('Preferences'), self.do_config) + acname = _('Change calibre behavior') if isosx else _('Preferences') + pm.addAction(QIcon(I('config.png')), acname, self.do_config) pm.addAction(QIcon(I('wizard.png')), _('Run welcome wizard'), self.gui.run_wizard) if not DEBUG: diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index 63d4499966..ca042ef64b 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -198,7 +198,7 @@ class MetadataSingleDialogBase(ResizableDialog): ans = self.custom_metadata_widgets for i in range(len(ans)-1): if before is not None and i == 0: - pass# Do something + pass if len(ans[i+1].widgets) == 2: sto(ans[i].widgets[-1], ans[i+1].widgets[1]) else: @@ -206,7 +206,7 @@ class MetadataSingleDialogBase(ResizableDialog): for c in range(2, len(ans[i].widgets), 2): sto(ans[i].widgets[c-1], ans[i].widgets[c+1]) if after is not None: - pass # Do something + pass # }}} def do_view_format(self, path, fmt): @@ -728,7 +728,135 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ # }}} -editors = {'default': MetadataSingleDialog, 'alt1': MetadataSingleDialogAlt1} +class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{ + + cc_two_column = False + one_line_comments_toolbar = True + + def do_layout(self): + self.central_widget.clear() + self.labels = [] + sto = QWidget.setTabOrder + + self.central_widget.tabBar().setVisible(False) + tab0 = QWidget(self) + self.central_widget.addTab(tab0, _("&Metadata")) + l = QGridLayout() + tab0.setLayout(l) + + # Basic metadata in col 0 + tl = QGridLayout() + gb = QGroupBox(_('Basic metadata'), tab0) + l.addWidget(gb, 0, 0, 1, 1) + gb.setLayout(tl) + + self.button_box.addButton(self.fetch_metadata_button, + QDialogButtonBox.ActionRole) + self.config_metadata_button.setToolButtonStyle(Qt.ToolButtonTextOnly) + self.config_metadata_button.setText(_('Configure metadata downloading')) + self.button_box.addButton(self.config_metadata_button, + QDialogButtonBox.ActionRole) + sto(self.button_box, self.title) + + def create_row(row, widget, tab_to, button=None, icon=None, span=1): + ql = BuddyLabel(widget) + tl.addWidget(ql, row, 1, 1, 1) + tl.addWidget(widget, row, 2, 1, 1) + if button is not None: + tl.addWidget(button, row, 3, span, 1) + if icon is not None: + button.setIcon(QIcon(I(icon))) + if tab_to is not None: + if button is not None: + sto(widget, button) + sto(button, tab_to) + else: + sto(widget, tab_to) + + tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) + + create_row(0, self.title, self.title_sort, + button=self.deduce_title_sort_button, span=2, + icon='auto_author_sort.png') + create_row(1, self.title_sort, self.authors) + create_row(2, self.authors, self.author_sort, + button=self.deduce_author_sort_button, + span=2, icon='auto_author_sort.png') + create_row(3, self.author_sort, self.series) + create_row(4, self.series, self.series_index, + button=self.remove_unused_series_button, icon='trash.png') + create_row(5, self.series_index, self.tags) + create_row(6, self.tags, self.rating, button=self.tags_editor_button) + create_row(7, self.rating, self.pubdate) + create_row(8, self.pubdate, self.publisher, + button=self.pubdate.clear_button, icon='trash.png') + create_row(9, self.publisher, self.timestamp) + create_row(10, self.timestamp, self.identifiers, + button=self.timestamp.clear_button, icon='trash.png') + create_row(11, self.identifiers, self.comments, + button=self.clear_identifiers_button, icon='trash.png') + tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), + 12, 1, 1 ,1) + + # Custom metadata in col 1 + w = getattr(self, 'custom_metadata_widgets_parent', None) + if w is not None: + gb = QGroupBox(_('Custom metadata'), tab0) + gbl = QVBoxLayout() + gb.setLayout(gbl) + sr = QScrollArea(gb) + sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + sr.setWidgetResizable(True) + sr.setBackgroundRole(QPalette.Base) + sr.setFrameStyle(QFrame.NoFrame) + sr.setWidget(w) + gbl.addWidget(sr) + l.addWidget(gb, 0, 1, 1, 1) + sp = QSizePolicy() + sp.setVerticalStretch(10) + sp.setHorizontalPolicy(QSizePolicy.Fixed) + sp.setVerticalPolicy(QSizePolicy.Expanding) + gb.setSizePolicy(sp) + self.set_custom_metadata_tab_order() + + # comments span col 0 & 1 + w = QGroupBox(_('Comments'), tab0) + sp = QSizePolicy() + sp.setVerticalStretch(10) + sp.setHorizontalPolicy(QSizePolicy.Expanding) + sp.setVerticalPolicy(QSizePolicy.Expanding) + w.setSizePolicy(sp) + lb = QHBoxLayout() + w.setLayout(lb) + lb.addWidget(self.comments) + l.addWidget(w, 1, 0, 1, 2) + + # Cover & formats in col 3 + gb = QGroupBox(_('Cover'), tab0) + lb = QGridLayout() + gb.setLayout(lb) + lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter) + sto(self.clear_identifiers_button, self.cover.buttons[0]) + for i, b in enumerate(self.cover.buttons[:3]): + lb.addWidget(b, 1, i, 1, 1) + sto(b, self.cover.buttons[i+1]) + hl = QHBoxLayout() + for b in self.cover.buttons[3:]: + hl.addWidget(b) + sto(self.cover.buttons[-2], self.cover.buttons[-1]) + lb.addLayout(hl, 2, 0, 1, 3) + l.addWidget(gb, 0, 2, 1, 1) + l.addWidget(self.formats_manager, 1, 2, 1, 1) + sto(self.cover.buttons[-1], self.formats_manager) + + self.formats_manager.formats.setMaximumWidth(10000) + self.formats_manager.formats.setIconSize(QSize(32, 32)) + +# }}} + + +editors = {'default': MetadataSingleDialog, 'alt1': MetadataSingleDialogAlt1, + 'alt2': MetadataSingleDialogAlt2} def edit_metadata(db, row_list, current_row, parent=None, view_slot=None, set_current_callback=None): diff --git a/src/calibre/gui2/preferences/behavior.py b/src/calibre/gui2/preferences/behavior.py index e062ae2662..1247c54ec9 100644 --- a/src/calibre/gui2/preferences/behavior.py +++ b/src/calibre/gui2/preferences/behavior.py @@ -61,7 +61,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('bools_are_tristate', db.prefs, restart_required=True) r = self.register - choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1')] + choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1'), + (_('All on 1 tab'), 'alt2')] r('edit_metadata_single_layout', gprefs, choices=choices) def initialize(self): diff --git a/src/calibre/gui2/store/__init__.py b/src/calibre/gui2/store/__init__.py index fd2fb965a9..214ede3372 100644 --- a/src/calibre/gui2/store/__init__.py +++ b/src/calibre/gui2/store/__init__.py @@ -47,7 +47,7 @@ class StorePlugin(object): # {{{ def __init__(self, gui, name): from calibre.gui2 import JSONConfig - + self.gui = gui self.name = name self.base_plugin = None @@ -79,14 +79,14 @@ class StorePlugin(object): # {{{ return items as a generator. Don't be lazy with the search! Load as much data as possible in the - :class:`calibre.gui2.store.search_result.SearchResult` object. + :class:`calibre.gui2.store.search_result.SearchResult` object. However, if data (such as cover_url) isn't available because the store does not display cover images then it's okay to ignore it. - + At the very least a :class:`calibre.gui2.store.search_result.SearchResult` returned by this function must have the title, author and id. - + If you have to parse multiple pages to get all of the data then implement :meth:`get_deatils` for retrieving additional information. @@ -105,24 +105,24 @@ class StorePlugin(object): # {{{ item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store. ''' raise NotImplementedError() - + def get_details(self, search_result, timeout=60): ''' Delayed search for information about specific search items. - + Typically, this will be used when certain information such as formats, drm status, cover url are not part of the main search results and the information is on another web page. - + Using this function allows for the main information (title, author) to be displayed in the search results while other information can take extra time to load. Splitting retrieving data that takes longer to load into a separate function will give the illusion of the search being faster. - + :param search_result: A search result that need details set. :param timeout: The maximum amount of time in seconds to spend downloading details. - + :return: True if the search_result was modified otherwise False ''' return False @@ -133,30 +133,30 @@ class StorePlugin(object): # {{{ is called to update the caches. It is recommended to call this function from :meth:`open`. Especially if :meth:`open` does anything other than open a web page. - + This function can be called at any time. It is up to the plugin to determine if the cache really does need updating. Unless :param:`force` is True, then the plugin must update the cache. The only time force should be True is if this function is called by the plugin's configuration dialog. - + if :param:`suppress_progress` is False it is safe to assume that this function is being called from the main GUI thread so it is safe and recommended to use a QProgressDialog to display what is happening and allow the user to cancel the operation. if :param:`suppress_progress` is True then run the update silently. In this case there is no guarantee what thread is calling this function so no Qt related functionality that requires being run in the main - GUI thread should be run. E.G. Open a QProgressDialog. - + GUI thread should be run. E.G. Open a QProgressDialog. + :param parent: The parent object to be used by an GUI dialogs. - + :param timeout: The maximum amount of time that should be spent in any given network connection. - + :param force: Force updating the cache even if the plugin has determined it is not necessary. - + :param suppress_progress: Should a progress indicator be shown. - + :return: True if the cache was updated, False otherwise. ''' return False