mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Initial import of get books store chooser dialog
This commit is contained in:
commit
8ed4ca3469
@ -854,14 +854,14 @@ class ActionStore(InterfaceActionBase):
|
|||||||
name = 'Store'
|
name = 'Store'
|
||||||
author = 'John Schember'
|
author = 'John Schember'
|
||||||
actual_plugin = 'calibre.gui2.actions.store:StoreAction'
|
actual_plugin = 'calibre.gui2.actions.store:StoreAction'
|
||||||
|
|
||||||
def customization_help(self, gui=False):
|
def customization_help(self, gui=False):
|
||||||
return 'Customize the behavior of the store search.'
|
return 'Customize the behavior of the store search.'
|
||||||
|
|
||||||
def config_widget(self):
|
def config_widget(self):
|
||||||
from calibre.gui2.store.config.store import config_widget as get_cw
|
from calibre.gui2.store.config.store import config_widget as get_cw
|
||||||
return get_cw()
|
return get_cw()
|
||||||
|
|
||||||
def save_settings(self, config_widget):
|
def save_settings(self, config_widget):
|
||||||
from calibre.gui2.store.config.store import save_settings as save
|
from calibre.gui2.store.config.store import save_settings as save
|
||||||
save(config_widget)
|
save(config_widget)
|
||||||
@ -1108,7 +1108,7 @@ class StoreAmazonKindleStore(StoreBase):
|
|||||||
name = 'Amazon Kindle'
|
name = 'Amazon Kindle'
|
||||||
description = u'Kindle books from Amazon.'
|
description = u'Kindle books from Amazon.'
|
||||||
actual_plugin = 'calibre.gui2.store.amazon_plugin:AmazonKindleStore'
|
actual_plugin = 'calibre.gui2.store.amazon_plugin:AmazonKindleStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['KINDLE']
|
formats = ['KINDLE']
|
||||||
@ -1118,7 +1118,7 @@ class StoreAmazonDEKindleStore(StoreBase):
|
|||||||
author = 'Charles Haley'
|
author = 'Charles Haley'
|
||||||
description = u'Kindle Bücher von Amazon.'
|
description = u'Kindle Bücher von Amazon.'
|
||||||
actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore'
|
actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'DE'
|
headquarters = 'DE'
|
||||||
formats = ['KINDLE']
|
formats = ['KINDLE']
|
||||||
@ -1128,7 +1128,7 @@ class StoreAmazonUKKindleStore(StoreBase):
|
|||||||
author = 'Charles Haley'
|
author = 'Charles Haley'
|
||||||
description = u'Kindle books from Amazon\'s UK web site. Also, includes French language ebooks.'
|
description = u'Kindle books from Amazon\'s UK web site. Also, includes French language ebooks.'
|
||||||
actual_plugin = 'calibre.gui2.store.amazon_uk_plugin:AmazonUKKindleStore'
|
actual_plugin = 'calibre.gui2.store.amazon_uk_plugin:AmazonUKKindleStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'UK'
|
headquarters = 'UK'
|
||||||
formats = ['KINDLE']
|
formats = ['KINDLE']
|
||||||
@ -1146,7 +1146,7 @@ class StoreBaenWebScriptionStore(StoreBase):
|
|||||||
name = 'Baen WebScription'
|
name = 'Baen WebScription'
|
||||||
description = u'Sci-Fi & Fantasy brought to you by Jim Baen.'
|
description = u'Sci-Fi & Fantasy brought to you by Jim Baen.'
|
||||||
actual_plugin = 'calibre.gui2.store.baen_webscription_plugin:BaenWebScriptionStore'
|
actual_plugin = 'calibre.gui2.store.baen_webscription_plugin:BaenWebScriptionStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'LIT', 'LRF', 'MOBI', 'RB', 'RTF', 'ZIP']
|
formats = ['EPUB', 'LIT', 'LRF', 'MOBI', 'RB', 'RTF', 'ZIP']
|
||||||
@ -1155,7 +1155,7 @@ class StoreBNStore(StoreBase):
|
|||||||
name = 'Barnes and Noble'
|
name = 'Barnes and Noble'
|
||||||
description = u'The world\'s largest book seller. As the ultimate destination for book lovers, Barnes & Noble.com offers an incredible array of content.'
|
description = u'The world\'s largest book seller. As the ultimate destination for book lovers, Barnes & Noble.com offers an incredible array of content.'
|
||||||
actual_plugin = 'calibre.gui2.store.bn_plugin:BNStore'
|
actual_plugin = 'calibre.gui2.store.bn_plugin:BNStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['NOOK']
|
formats = ['NOOK']
|
||||||
@ -1163,9 +1163,9 @@ class StoreBNStore(StoreBase):
|
|||||||
class StoreBeamEBooksDEStore(StoreBase):
|
class StoreBeamEBooksDEStore(StoreBase):
|
||||||
name = 'Beam EBooks DE'
|
name = 'Beam EBooks DE'
|
||||||
author = 'Charles Haley'
|
author = 'Charles Haley'
|
||||||
description = u'Der eBook Shop.'
|
description = u'Bei uns finden Sie: Tausende deutschsprachige eBooks; Alle eBooks ohne hartes DRM; PDF, ePub und Mobipocket Format; Sofortige Verfügbarkeit - 24 Stunden am Tag; Günstige Preise; eBooks für viele Lesegeräte, PC,Mac und Smartphones; Viele Gratis eBooks'
|
||||||
actual_plugin = 'calibre.gui2.store.beam_ebooks_de_plugin:BeamEBooksDEStore'
|
actual_plugin = 'calibre.gui2.store.beam_ebooks_de_plugin:BeamEBooksDEStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'DE'
|
headquarters = 'DE'
|
||||||
formats = ['EPUB', 'MOBI', 'PDF']
|
formats = ['EPUB', 'MOBI', 'PDF']
|
||||||
@ -1174,7 +1174,7 @@ class StoreBeWriteStore(StoreBase):
|
|||||||
name = 'BeWrite Books'
|
name = 'BeWrite Books'
|
||||||
description = u'Publishers of fine books. Highly selective and editorially driven. Does not offer: books for children or exclusively YA, erotica, swords-and-sorcery fantasy and space-opera-style science fiction. All other genres are represented.'
|
description = u'Publishers of fine books. Highly selective and editorially driven. Does not offer: books for children or exclusively YA, erotica, swords-and-sorcery fantasy and space-opera-style science fiction. All other genres are represented.'
|
||||||
actual_plugin = 'calibre.gui2.store.bewrite_plugin:BeWriteStore'
|
actual_plugin = 'calibre.gui2.store.bewrite_plugin:BeWriteStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'MOBI', 'PDF']
|
formats = ['EPUB', 'MOBI', 'PDF']
|
||||||
@ -1183,7 +1183,7 @@ class StoreDieselEbooksStore(StoreBase):
|
|||||||
name = 'Diesel eBooks'
|
name = 'Diesel eBooks'
|
||||||
description = u'Instant access to over 2.4 million titles from hundreds of publishers including Harlequin, HarperCollins, John Wiley & Sons, McGraw-Hill, Simon & Schuster and Random House.'
|
description = u'Instant access to over 2.4 million titles from hundreds of publishers including Harlequin, HarperCollins, John Wiley & Sons, McGraw-Hill, Simon & Schuster and Random House.'
|
||||||
actual_plugin = 'calibre.gui2.store.diesel_ebooks_plugin:DieselEbooksStore'
|
actual_plugin = 'calibre.gui2.store.diesel_ebooks_plugin:DieselEbooksStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'PDF']
|
formats = ['EPUB', 'PDF']
|
||||||
@ -1192,7 +1192,7 @@ class StoreEbookscomStore(StoreBase):
|
|||||||
name = 'eBooks.com'
|
name = 'eBooks.com'
|
||||||
description = u'Sells books in multiple electronic formats in all categories. Technical infrastructure is cutting edge, robust and scalable, with servers in the US and Europe.'
|
description = u'Sells books in multiple electronic formats in all categories. Technical infrastructure is cutting edge, robust and scalable, with servers in the US and Europe.'
|
||||||
actual_plugin = 'calibre.gui2.store.ebooks_com_plugin:EbookscomStore'
|
actual_plugin = 'calibre.gui2.store.ebooks_com_plugin:EbookscomStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'LIT', 'MOBI', 'PDF']
|
formats = ['EPUB', 'LIT', 'MOBI', 'PDF']
|
||||||
@ -1200,9 +1200,9 @@ class StoreEbookscomStore(StoreBase):
|
|||||||
class StoreEPubBuyDEStore(StoreBase):
|
class StoreEPubBuyDEStore(StoreBase):
|
||||||
name = 'EPUBBuy DE'
|
name = 'EPUBBuy DE'
|
||||||
author = 'Charles Haley'
|
author = 'Charles Haley'
|
||||||
description = u'Deutsch epub-Spezialisten.'
|
description = u'Bei EPUBBuy.com finden Sie ausschliesslich eBooks im weitverbreiteten EPUB-Format und ohne DRM. So haben Sie die freie Wahl, wo Sie Ihr eBook lesen: Tablet, eBook-Reader, Smartphone oder einfach auf Ihrem PC. So macht eBook-Lesen Spaß!'
|
||||||
actual_plugin = 'calibre.gui2.store.epubbuy_de_plugin:EPubBuyDEStore'
|
actual_plugin = 'calibre.gui2.store.epubbuy_de_plugin:EPubBuyDEStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'DE'
|
headquarters = 'DE'
|
||||||
formats = ['EPUB']
|
formats = ['EPUB']
|
||||||
@ -1211,7 +1211,7 @@ class StoreEHarlequinStore(StoreBase):
|
|||||||
name = 'eHarlequin'
|
name = 'eHarlequin'
|
||||||
description = u'A global leader in series romance and one of the world\'s leading publishers of books for women. Offers women a broad range of reading from romance to bestseller fiction, from young adult novels to erotic literature, from nonfiction to fantasy, from African-American novels to inspirational romance, and more.'
|
description = u'A global leader in series romance and one of the world\'s leading publishers of books for women. Offers women a broad range of reading from romance to bestseller fiction, from young adult novels to erotic literature, from nonfiction to fantasy, from African-American novels to inspirational romance, and more.'
|
||||||
actual_plugin = 'calibre.gui2.store.eharlequin_plugin:EHarlequinStore'
|
actual_plugin = 'calibre.gui2.store.eharlequin_plugin:EHarlequinStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'CA'
|
headquarters = 'CA'
|
||||||
formats = ['EPUB', 'PDF']
|
formats = ['EPUB', 'PDF']
|
||||||
@ -1220,7 +1220,7 @@ class StoreFeedbooksStore(StoreBase):
|
|||||||
name = 'Feedbooks'
|
name = 'Feedbooks'
|
||||||
description = u'Feedbooks is a cloud publishing and distribution service, connected to a large ecosystem of reading systems and social networks. Provides a variety of genres from independent and classic books.'
|
description = u'Feedbooks is a cloud publishing and distribution service, connected to a large ecosystem of reading systems and social networks. Provides a variety of genres from independent and classic books.'
|
||||||
actual_plugin = 'calibre.gui2.store.feedbooks_plugin:FeedbooksStore'
|
actual_plugin = 'calibre.gui2.store.feedbooks_plugin:FeedbooksStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'FR'
|
headquarters = 'FR'
|
||||||
formats = ['EPUB', 'MOBI', 'PDF']
|
formats = ['EPUB', 'MOBI', 'PDF']
|
||||||
@ -1249,7 +1249,7 @@ class StoreGoogleBooksStore(StoreBase):
|
|||||||
name = 'Google Books'
|
name = 'Google Books'
|
||||||
description = u'Google Books'
|
description = u'Google Books'
|
||||||
actual_plugin = 'calibre.gui2.store.google_books_plugin:GoogleBooksStore'
|
actual_plugin = 'calibre.gui2.store.google_books_plugin:GoogleBooksStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'PDF', 'TXT']
|
formats = ['EPUB', 'PDF', 'TXT']
|
||||||
@ -1258,7 +1258,7 @@ class StoreGutenbergStore(StoreBase):
|
|||||||
name = 'Project Gutenberg'
|
name = 'Project Gutenberg'
|
||||||
description = u'The first producer of free ebooks. Free in the United States because their copyright has expired. They may not be free of copyright in other countries. Readers outside of the United States must check the copyright laws of their countries before downloading or redistributing our ebooks.'
|
description = u'The first producer of free ebooks. Free in the United States because their copyright has expired. They may not be free of copyright in other countries. Readers outside of the United States must check the copyright laws of their countries before downloading or redistributing our ebooks.'
|
||||||
actual_plugin = 'calibre.gui2.store.gutenberg_plugin:GutenbergStore'
|
actual_plugin = 'calibre.gui2.store.gutenberg_plugin:GutenbergStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'HTML', 'MOBI', 'PDB', 'TXT']
|
formats = ['EPUB', 'HTML', 'MOBI', 'PDB', 'TXT']
|
||||||
@ -1267,16 +1267,26 @@ class StoreKoboStore(StoreBase):
|
|||||||
name = 'Kobo'
|
name = 'Kobo'
|
||||||
description = u'With over 2.3 million eBooks to browse we have engaged readers in over 200 countries in Kobo eReading. Our eBook listings include New York Times Bestsellers, award winners, classics and more!'
|
description = u'With over 2.3 million eBooks to browse we have engaged readers in over 200 countries in Kobo eReading. Our eBook listings include New York Times Bestsellers, award winners, classics and more!'
|
||||||
actual_plugin = 'calibre.gui2.store.kobo_plugin:KoboStore'
|
actual_plugin = 'calibre.gui2.store.kobo_plugin:KoboStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'CA'
|
headquarters = 'CA'
|
||||||
formats = ['EPUB']
|
formats = ['EPUB']
|
||||||
|
|
||||||
|
class StoreLegimiStore(StoreBase):
|
||||||
|
name = 'Legimi'
|
||||||
|
author = u'Tomasz Długosz'
|
||||||
|
description = u'Tanie oraz darmowe ebooki, egazety i blogi w formacie EPUB, wprost na Twój e-czytnik, iPhone, iPad, Android i komputer'
|
||||||
|
actual_plugin = 'calibre.gui2.store.legimi_plugin:LegimiStore'
|
||||||
|
|
||||||
|
drm_free_only = False
|
||||||
|
headquarters = 'PL'
|
||||||
|
formats = ['EPUB']
|
||||||
|
|
||||||
class StoreManyBooksStore(StoreBase):
|
class StoreManyBooksStore(StoreBase):
|
||||||
name = 'ManyBooks'
|
name = 'ManyBooks'
|
||||||
description = u'Public domain and creative commons works from many sources.'
|
description = u'Public domain and creative commons works from many sources.'
|
||||||
actual_plugin = 'calibre.gui2.store.manybooks_plugin:ManyBooksStore'
|
actual_plugin = 'calibre.gui2.store.manybooks_plugin:ManyBooksStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP']
|
formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP']
|
||||||
@ -1295,7 +1305,7 @@ class StoreNextoStore(StoreBase):
|
|||||||
author = u'Tomasz Długosz'
|
author = u'Tomasz Długosz'
|
||||||
description = u'Największy w Polsce sklep internetowy z audiobookami mp3, ebookami pdf oraz prasą do pobrania on-line.'
|
description = u'Największy w Polsce sklep internetowy z audiobookami mp3, ebookami pdf oraz prasą do pobrania on-line.'
|
||||||
actual_plugin = 'calibre.gui2.store.nexto_plugin:NextoStore'
|
actual_plugin = 'calibre.gui2.store.nexto_plugin:NextoStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'PL'
|
headquarters = 'PL'
|
||||||
formats = ['EPUB', 'PDF']
|
formats = ['EPUB', 'PDF']
|
||||||
@ -1304,16 +1314,16 @@ class StoreOpenLibraryStore(StoreBase):
|
|||||||
name = 'Open Library'
|
name = 'Open Library'
|
||||||
description = u'One web page for every book ever published. The goal is to be a true online library. Over 20 million records from a variety of large catalogs as well as single contributions, with more on the way.'
|
description = u'One web page for every book ever published. The goal is to be a true online library. Over 20 million records from a variety of large catalogs as well as single contributions, with more on the way.'
|
||||||
actual_plugin = 'calibre.gui2.store.open_library_plugin:OpenLibraryStore'
|
actual_plugin = 'calibre.gui2.store.open_library_plugin:OpenLibraryStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = ['US']
|
headquarters = 'US'
|
||||||
formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT']
|
formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT']
|
||||||
|
|
||||||
class StoreOReillyStore(StoreBase):
|
class StoreOReillyStore(StoreBase):
|
||||||
name = 'OReilly'
|
name = 'OReilly'
|
||||||
description = u'Programming and tech ebooks from OReilly.'
|
description = u'Programming and tech ebooks from OReilly.'
|
||||||
actual_plugin = 'calibre.gui2.store.oreilly_plugin:OReillyStore'
|
actual_plugin = 'calibre.gui2.store.oreilly_plugin:OReillyStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['APK', 'DAISY', 'EPUB', 'MOBI', 'PDF']
|
formats = ['APK', 'DAISY', 'EPUB', 'MOBI', 'PDF']
|
||||||
@ -1331,7 +1341,7 @@ class StoreSmashwordsStore(StoreBase):
|
|||||||
name = 'Smashwords'
|
name = 'Smashwords'
|
||||||
description = u'An ebook publishing and distribution platform for ebook authors, publishers and readers. Covers many genres and formats.'
|
description = u'An ebook publishing and distribution platform for ebook authors, publishers and readers. Covers many genres and formats.'
|
||||||
actual_plugin = 'calibre.gui2.store.smashwords_plugin:SmashwordsStore'
|
actual_plugin = 'calibre.gui2.store.smashwords_plugin:SmashwordsStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'US'
|
headquarters = 'US'
|
||||||
formats = ['EPUB', 'HTML', 'LRF', 'MOBI', 'PDB', 'RTF', 'TXT']
|
formats = ['EPUB', 'HTML', 'LRF', 'MOBI', 'PDB', 'RTF', 'TXT']
|
||||||
@ -1341,7 +1351,7 @@ class StoreWaterstonesUKStore(StoreBase):
|
|||||||
author = 'Charles Haley'
|
author = 'Charles Haley'
|
||||||
description = u'Waterstone\'s mission is to be the leading Bookseller on the High Street and online providing customers the widest choice, great value and expert advice from a team passionate about Bookselling.'
|
description = u'Waterstone\'s mission is to be the leading Bookseller on the High Street and online providing customers the widest choice, great value and expert advice from a team passionate about Bookselling.'
|
||||||
actual_plugin = 'calibre.gui2.store.waterstones_uk_plugin:WaterstonesUKStore'
|
actual_plugin = 'calibre.gui2.store.waterstones_uk_plugin:WaterstonesUKStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
headquarters = 'UK'
|
headquarters = 'UK'
|
||||||
formats = ['EPUB', 'PDF']
|
formats = ['EPUB', 'PDF']
|
||||||
@ -1359,7 +1369,7 @@ class StoreWizardsTowerBooksStore(StoreBase):
|
|||||||
name = 'Wizards Tower Books'
|
name = 'Wizards Tower Books'
|
||||||
description = u'A science fiction and fantasy publisher. Concentrates mainly on making out-of-print works available once more as e-books, and helping other small presses exploit the e-book market. Also publishes a small number of limited-print-run anthologies with a view to encouraging diversity in the science fiction and fantasy field.'
|
description = u'A science fiction and fantasy publisher. Concentrates mainly on making out-of-print works available once more as e-books, and helping other small presses exploit the e-book market. Also publishes a small number of limited-print-run anthologies with a view to encouraging diversity in the science fiction and fantasy field.'
|
||||||
actual_plugin = 'calibre.gui2.store.wizards_tower_books_plugin:WizardsTowerBooksStore'
|
actual_plugin = 'calibre.gui2.store.wizards_tower_books_plugin:WizardsTowerBooksStore'
|
||||||
|
|
||||||
drm_free_only = True
|
drm_free_only = True
|
||||||
headquarters = 'UK'
|
headquarters = 'UK'
|
||||||
formats = ['EPUB', 'MOBI']
|
formats = ['EPUB', 'MOBI']
|
||||||
@ -1371,7 +1381,7 @@ class StoreWoblinkStore(StoreBase):
|
|||||||
actual_plugin = 'calibre.gui2.store.woblink_plugin:WoblinkStore'
|
actual_plugin = 'calibre.gui2.store.woblink_plugin:WoblinkStore'
|
||||||
|
|
||||||
drm_free_only = False
|
drm_free_only = False
|
||||||
location = 'PL'
|
headquarters = 'PL'
|
||||||
formats = ['EPUB']
|
formats = ['EPUB']
|
||||||
|
|
||||||
plugins += [
|
plugins += [
|
||||||
@ -1389,10 +1399,11 @@ plugins += [
|
|||||||
StoreEHarlequinStore,
|
StoreEHarlequinStore,
|
||||||
StoreFeedbooksStore,
|
StoreFeedbooksStore,
|
||||||
StoreFoylesUKStore,
|
StoreFoylesUKStore,
|
||||||
StoreGandalfStore,
|
StoreGandalfStore,
|
||||||
StoreGoogleBooksStore,
|
StoreGoogleBooksStore,
|
||||||
StoreGutenbergStore,
|
StoreGutenbergStore,
|
||||||
StoreKoboStore,
|
StoreKoboStore,
|
||||||
|
StoreLegimiStore,
|
||||||
StoreManyBooksStore,
|
StoreManyBooksStore,
|
||||||
StoreMobileReadStore,
|
StoreMobileReadStore,
|
||||||
StoreNextoStore,
|
StoreNextoStore,
|
||||||
|
@ -34,6 +34,8 @@ class StoreAction(InterfaceAction):
|
|||||||
self.store_list_menu = self.store_menu.addMenu(_('Stores'))
|
self.store_list_menu = self.store_menu.addMenu(_('Stores'))
|
||||||
for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()):
|
for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()):
|
||||||
self.store_list_menu.addAction(n, partial(self.open_store, p))
|
self.store_list_menu.addAction(n, partial(self.open_store, p))
|
||||||
|
self.store_menu.addSeparator()
|
||||||
|
self.store_menu.addAction(_('Choose stores'), self.choose)
|
||||||
self.qaction.setMenu(self.store_menu)
|
self.qaction.setMenu(self.store_menu)
|
||||||
|
|
||||||
def do_search(self):
|
def do_search(self):
|
||||||
@ -107,6 +109,13 @@ class StoreAction(InterfaceAction):
|
|||||||
query = 'author:"%s" title:"%s"' % (self._get_author(row), self._get_title(row))
|
query = 'author:"%s" title:"%s"' % (self._get_author(row), self._get_title(row))
|
||||||
self.search(query)
|
self.search(query)
|
||||||
|
|
||||||
|
def choose(self):
|
||||||
|
from calibre.gui2.store.config.chooser.chooser_dialog import StoreChooserDialog
|
||||||
|
d = StoreChooserDialog(self.gui)
|
||||||
|
d.exec_()
|
||||||
|
self.gui.load_store_plugins()
|
||||||
|
self.load_menu()
|
||||||
|
|
||||||
def open_store(self, store_plugin):
|
def open_store(self, store_plugin):
|
||||||
self.show_disclaimer()
|
self.show_disclaimer()
|
||||||
store_plugin.open(self.gui)
|
store_plugin.open(self.gui)
|
||||||
|
0
src/calibre/gui2/store/config/chooser/__init__.py
Normal file
0
src/calibre/gui2/store/config/chooser/__init__.py
Normal file
131
src/calibre/gui2/store/config/chooser/adv_search_builder.py
Normal file
131
src/calibre/gui2/store/config/chooser/adv_search_builder.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QDialog, QDialogButtonBox)
|
||||||
|
|
||||||
|
from calibre.gui2.store.config.chooser.adv_search_builder_ui import Ui_Dialog
|
||||||
|
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
|
||||||
|
|
||||||
|
class AdvSearchBuilderDialog(QDialog, Ui_Dialog):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.buttonBox.accepted.connect(self.advanced_search_button_pushed)
|
||||||
|
self.tab_2_button_box.accepted.connect(self.accept)
|
||||||
|
self.tab_2_button_box.rejected.connect(self.reject)
|
||||||
|
self.clear_button.clicked.connect(self.clear_button_pushed)
|
||||||
|
self.adv_search_used = False
|
||||||
|
self.mc = ''
|
||||||
|
|
||||||
|
self.tabWidget.setCurrentIndex(0)
|
||||||
|
self.tabWidget.currentChanged[int].connect(self.tab_changed)
|
||||||
|
self.tab_changed(0)
|
||||||
|
|
||||||
|
def tab_changed(self, idx):
|
||||||
|
if idx == 1:
|
||||||
|
self.tab_2_button_box.button(QDialogButtonBox.Ok).setDefault(True)
|
||||||
|
else:
|
||||||
|
self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True)
|
||||||
|
|
||||||
|
def advanced_search_button_pushed(self):
|
||||||
|
self.adv_search_used = True
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def clear_button_pushed(self):
|
||||||
|
self.name_box.setText('')
|
||||||
|
self.description_box.setText('')
|
||||||
|
self.headquarters_box.setText('')
|
||||||
|
self.format_box.setText('')
|
||||||
|
self.enabled_combo.setIndex(0)
|
||||||
|
self.drm_combo.setIndex(0)
|
||||||
|
|
||||||
|
def tokens(self, raw):
|
||||||
|
phrases = re.findall(r'\s*".*?"\s*', raw)
|
||||||
|
for f in phrases:
|
||||||
|
raw = raw.replace(f, ' ')
|
||||||
|
phrases = [t.strip('" ') for t in phrases]
|
||||||
|
return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]]
|
||||||
|
|
||||||
|
def search_string(self):
|
||||||
|
if self.adv_search_used:
|
||||||
|
return self.adv_search_string()
|
||||||
|
else:
|
||||||
|
return self.box_search_string()
|
||||||
|
|
||||||
|
def adv_search_string(self):
|
||||||
|
mk = self.matchkind.currentIndex()
|
||||||
|
if mk == CONTAINS_MATCH:
|
||||||
|
self.mc = ''
|
||||||
|
elif mk == EQUALS_MATCH:
|
||||||
|
self.mc = '='
|
||||||
|
else:
|
||||||
|
self.mc = '~'
|
||||||
|
all, any, phrase, none = map(lambda x: unicode(x.text()),
|
||||||
|
(self.all, self.any, self.phrase, self.none))
|
||||||
|
all, any, none = map(self.tokens, (all, any, none))
|
||||||
|
phrase = phrase.strip()
|
||||||
|
all = ' and '.join(all)
|
||||||
|
any = ' or '.join(any)
|
||||||
|
none = ' and not '.join(none)
|
||||||
|
ans = ''
|
||||||
|
if phrase:
|
||||||
|
ans += '"%s"'%phrase
|
||||||
|
if all:
|
||||||
|
ans += (' and ' if ans else '') + all
|
||||||
|
if none:
|
||||||
|
ans += (' and not ' if ans else 'not ') + none
|
||||||
|
if any:
|
||||||
|
ans += (' or ' if ans else '') + any
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def token(self):
|
||||||
|
txt = unicode(self.text.text()).strip()
|
||||||
|
if txt:
|
||||||
|
if self.negate.isChecked():
|
||||||
|
txt = '!'+txt
|
||||||
|
tok = self.FIELDS[unicode(self.field.currentText())]+txt
|
||||||
|
if re.search(r'\s', tok):
|
||||||
|
tok = '"%s"'%tok
|
||||||
|
return tok
|
||||||
|
|
||||||
|
def box_search_string(self):
|
||||||
|
mk = self.matchkind.currentIndex()
|
||||||
|
if mk == CONTAINS_MATCH:
|
||||||
|
self.mc = ''
|
||||||
|
elif mk == EQUALS_MATCH:
|
||||||
|
self.mc = '='
|
||||||
|
else:
|
||||||
|
self.mc = '~'
|
||||||
|
|
||||||
|
ans = []
|
||||||
|
self.box_last_values = {}
|
||||||
|
name = unicode(self.name_box.text()).strip()
|
||||||
|
if name:
|
||||||
|
ans.append('name:"' + self.mc + name + '"')
|
||||||
|
description = unicode(self.description_box.text()).strip()
|
||||||
|
if description:
|
||||||
|
ans.append('description:"' + self.mc + description + '"')
|
||||||
|
headquarters = unicode(self.headquarters_box.text()).strip()
|
||||||
|
if headquarters:
|
||||||
|
ans.append('headquarters:"' + self.mc + headquarters + '"')
|
||||||
|
format = unicode(self.format_box.text()).strip()
|
||||||
|
if format:
|
||||||
|
ans.append('format:"' + self.mc + format + '"')
|
||||||
|
enabled = unicode(self.enabled_combo.currentText()).strip()
|
||||||
|
if enabled:
|
||||||
|
ans.append('enabled:' + enabled)
|
||||||
|
drm = unicode(self.drm_combo.currentText()).strip()
|
||||||
|
if drm:
|
||||||
|
ans.append('drm:' + drm)
|
||||||
|
if ans:
|
||||||
|
return ' and '.join(ans)
|
||||||
|
return ''
|
416
src/calibre/gui2/store/config/chooser/adv_search_builder.ui
Normal file
416
src/calibre/gui2/store/config/chooser/adv_search_builder.ui
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>752</width>
|
||||||
|
<height>472</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Advanced Search</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset>
|
||||||
|
<normaloff>:/images/search.png</normaloff>:/images/search.png</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>&What kind of match to use:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>matchkind</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="matchkind">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Contains: the word or phrase matches anywhere in the metadata field</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Equals: the word or phrase must match the entire metadata field</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Regular expression: the expression must match anywhere in the metadata field</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>A&dvanced Search</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Find entries that have...</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>&All these words:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="all"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>This exact &phrase:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="phrase"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>&One or more of these words:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="any"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>But dont show entries that have...</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Any of these &unwanted words:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="none"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>See the <a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual</a> for more help</string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Nam&e/Description ...</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Name:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>name_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="EnLineEdit" name="name_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enter the title.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Description:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>description_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="price_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Headquarters:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>headquarters_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0" colspan="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="clear_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Clear</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="tab_2_button_box">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>Search only in specific fields:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="EnLineEdit" name="description_box"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="format_box"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Format:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>format_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="EnLineEdit" name="headquarters_box"/>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enabled:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="text">
|
||||||
|
<string>DRM:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QComboBox" name="enabled_combo">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>true</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>false</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QComboBox" name="drm_combo">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>true</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>false</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>EnLineEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>widgets.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>all</tabstop>
|
||||||
|
<tabstop>phrase</tabstop>
|
||||||
|
<tabstop>any</tabstop>
|
||||||
|
<tabstop>none</tabstop>
|
||||||
|
<tabstop>buttonBox</tabstop>
|
||||||
|
<tabstop>name_box</tabstop>
|
||||||
|
<tabstop>description_box</tabstop>
|
||||||
|
<tabstop>headquarters_box</tabstop>
|
||||||
|
<tabstop>format_box</tabstop>
|
||||||
|
<tabstop>clear_button</tabstop>
|
||||||
|
<tabstop>tab_2_button_box</tabstop>
|
||||||
|
<tabstop>tabWidget</tabstop>
|
||||||
|
<tabstop>matchkind</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>Dialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>Dialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
28
src/calibre/gui2/store/config/chooser/chooser_dialog.py
Normal file
28
src/calibre/gui2/store/config/chooser/chooser_dialog.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QDialog, QDialogButtonBox, QVBoxLayout)
|
||||||
|
|
||||||
|
from calibre.gui2.store.config.chooser.chooser_widget import StoreChooserWidget
|
||||||
|
|
||||||
|
class StoreChooserDialog(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
|
||||||
|
self.setWindowTitle(_('Choose stores'))
|
||||||
|
|
||||||
|
button_box = QDialogButtonBox(QDialogButtonBox.Close)
|
||||||
|
button_box.accepted.connect(self.accept)
|
||||||
|
button_box.rejected.connect(self.reject)
|
||||||
|
v = QVBoxLayout(self)
|
||||||
|
self.config_widget = StoreChooserWidget()
|
||||||
|
v.addWidget(self.config_widget)
|
||||||
|
v.addWidget(button_box)
|
||||||
|
|
||||||
|
self.resize(800, 600)
|
35
src/calibre/gui2/store/config/chooser/chooser_widget.py
Normal file
35
src/calibre/gui2/store/config/chooser/chooser_widget.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QWidget, QIcon, QDialog)
|
||||||
|
|
||||||
|
from calibre.gui2.store.config.chooser.adv_search_builder import AdvSearchBuilderDialog
|
||||||
|
from calibre.gui2.store.config.chooser.chooser_widget_ui import Ui_Form
|
||||||
|
|
||||||
|
class StoreChooserWidget(QWidget, Ui_Form):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.adv_search_builder.setIcon(QIcon(I('search.png')))
|
||||||
|
|
||||||
|
self.search.clicked.connect(self.do_search)
|
||||||
|
self.adv_search_builder.clicked.connect(self.build_adv_search)
|
||||||
|
self.results_view.activated.connect(self.toggle_plugin)
|
||||||
|
|
||||||
|
def do_search(self):
|
||||||
|
self.results_view.model().search(unicode(self.query.text()))
|
||||||
|
|
||||||
|
def toggle_plugin(self, index):
|
||||||
|
self.results_view.model().toggle_plugin(index)
|
||||||
|
|
||||||
|
def build_adv_search(self):
|
||||||
|
adv = AdvSearchBuilderDialog(self)
|
||||||
|
if adv.exec_() == QDialog.Accepted:
|
||||||
|
self.query.setText(adv.search_string())
|
87
src/calibre/gui2/store/config/chooser/chooser_widget.ui
Normal file
87
src/calibre/gui2/store/config/chooser/chooser_widget.ui
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>610</width>
|
||||||
|
<height>553</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Query:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="adv_search_builder">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="query"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="search">
|
||||||
|
<property name="text">
|
||||||
|
<string>Search</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="ResultsView" name="results_view">
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::SingleSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="rootIsDecorated">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="uniformRowHeights">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="itemsExpandable">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="expandsOnDoubleClick">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="headerStretchLastSection">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>ResultsView</class>
|
||||||
|
<extends>QTreeView</extends>
|
||||||
|
<header>results_view.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
244
src/calibre/gui2/store/config/chooser/models.py
Normal file
244
src/calibre/gui2/store/config/chooser/models.py
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import (Qt, QAbstractItemModel, QIcon, QVariant, QModelIndex)
|
||||||
|
|
||||||
|
from calibre.gui2 import NONE
|
||||||
|
from calibre.customize.ui import is_disabled, disable_plugin, enable_plugin
|
||||||
|
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
||||||
|
REGEXP_MATCH
|
||||||
|
from calibre.utils.icu import sort_key
|
||||||
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
|
|
||||||
|
|
||||||
|
class Matches(QAbstractItemModel):
|
||||||
|
|
||||||
|
HEADERS = [_('Enabled'), _('Name'), _('No DRM'), _('Headquarters'), _('Formats')]
|
||||||
|
HTML_COLS = [1]
|
||||||
|
|
||||||
|
def __init__(self, plugins):
|
||||||
|
QAbstractItemModel.__init__(self)
|
||||||
|
|
||||||
|
self.NO_DRM_ICON = QIcon(I('ok.png'))
|
||||||
|
|
||||||
|
self.all_matches = plugins
|
||||||
|
self.matches = plugins
|
||||||
|
self.filter = ''
|
||||||
|
self.search_filter = SearchFilter(self.all_matches)
|
||||||
|
|
||||||
|
self.sort_col = 1
|
||||||
|
self.sort_order = Qt.AscendingOrder
|
||||||
|
|
||||||
|
def get_plugin(self, index):
|
||||||
|
row = index.row()
|
||||||
|
if row < len(self.matches):
|
||||||
|
return self.matches[row]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def search(self, filter):
|
||||||
|
self.filter = filter.strip()
|
||||||
|
if not self.filter:
|
||||||
|
self.matches = self.all_matches
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.matches = list(self.search_filter.parse(self.filter))
|
||||||
|
except:
|
||||||
|
self.matches = self.all_matches
|
||||||
|
self.layoutChanged.emit()
|
||||||
|
self.sort(self.sort_col, self.sort_order)
|
||||||
|
|
||||||
|
def toggle_plugin(self, index):
|
||||||
|
new_index = self.createIndex(index.row(), 0)
|
||||||
|
data = QVariant(is_disabled(self.get_plugin(index)))
|
||||||
|
self.setData(new_index, data, Qt.CheckStateRole)
|
||||||
|
|
||||||
|
def index(self, row, column, parent=QModelIndex()):
|
||||||
|
return self.createIndex(row, column)
|
||||||
|
|
||||||
|
def parent(self, index):
|
||||||
|
if not index.isValid() or index.internalId() == 0:
|
||||||
|
return QModelIndex()
|
||||||
|
return self.createIndex(0, 0)
|
||||||
|
|
||||||
|
def rowCount(self, *args):
|
||||||
|
return len(self.matches)
|
||||||
|
|
||||||
|
def columnCount(self, *args):
|
||||||
|
return len(self.HEADERS)
|
||||||
|
|
||||||
|
def headerData(self, section, orientation, role):
|
||||||
|
if role != Qt.DisplayRole:
|
||||||
|
return NONE
|
||||||
|
text = ''
|
||||||
|
if orientation == Qt.Horizontal:
|
||||||
|
if section < len(self.HEADERS):
|
||||||
|
text = self.HEADERS[section]
|
||||||
|
return QVariant(text)
|
||||||
|
else:
|
||||||
|
return QVariant(section+1)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
row, col = index.row(), index.column()
|
||||||
|
result = self.matches[row]
|
||||||
|
if role in (Qt.DisplayRole, Qt.EditRole):
|
||||||
|
if col == 1:
|
||||||
|
return QVariant('<b>%s</b><br><i>%s</i>' % (result.name, result.description))
|
||||||
|
elif col == 3:
|
||||||
|
return QVariant(result.headquarters)
|
||||||
|
elif col == 4:
|
||||||
|
return QVariant(', '.join(result.formats).upper())
|
||||||
|
elif role == Qt.DecorationRole:
|
||||||
|
if col == 2:
|
||||||
|
if result.drm_free_only:
|
||||||
|
return QVariant(self.NO_DRM_ICON)
|
||||||
|
elif role == Qt.CheckStateRole:
|
||||||
|
if col == 0:
|
||||||
|
if is_disabled(result):
|
||||||
|
return Qt.Unchecked
|
||||||
|
return Qt.Checked
|
||||||
|
elif role == Qt.ToolTipRole:
|
||||||
|
return QVariant('<p>%s</p>' % result.description)
|
||||||
|
return NONE
|
||||||
|
|
||||||
|
def setData(self, index, data, role):
|
||||||
|
if not index.isValid():
|
||||||
|
return False
|
||||||
|
row, col = index.row(), index.column()
|
||||||
|
if col == 0:
|
||||||
|
if data.toBool():
|
||||||
|
enable_plugin(self.get_plugin(index))
|
||||||
|
else:
|
||||||
|
disable_plugin(self.get_plugin(index))
|
||||||
|
self.dataChanged.emit(self.index(index.row(), 0), self.index(index.row(), self.columnCount() - 1))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def flags(self, index):
|
||||||
|
if index.column() == 0:
|
||||||
|
return QAbstractItemModel.flags(self, index) | Qt.ItemIsUserCheckable
|
||||||
|
return QAbstractItemModel.flags(self, index)
|
||||||
|
|
||||||
|
def data_as_text(self, match, col):
|
||||||
|
text = ''
|
||||||
|
if col == 0:
|
||||||
|
text = 'b' if is_disabled(match) else 'a'
|
||||||
|
elif col == 1:
|
||||||
|
text = match.name
|
||||||
|
elif col == 2:
|
||||||
|
text = 'b' if getattr(match, 'drm', True) else 'a'
|
||||||
|
elif col == 3:
|
||||||
|
text = getattr(match, 'headquarters', '')
|
||||||
|
return text
|
||||||
|
|
||||||
|
def sort(self, col, order, reset=True):
|
||||||
|
self.sort_col = col
|
||||||
|
self.sort_order = order
|
||||||
|
if not self.matches:
|
||||||
|
return
|
||||||
|
descending = order == Qt.DescendingOrder
|
||||||
|
self.matches.sort(None,
|
||||||
|
lambda x: sort_key(unicode(self.data_as_text(x, col))),
|
||||||
|
descending)
|
||||||
|
if reset:
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
|
||||||
|
class SearchFilter(SearchQueryParser):
|
||||||
|
|
||||||
|
USABLE_LOCATIONS = [
|
||||||
|
'all',
|
||||||
|
'description',
|
||||||
|
'drm',
|
||||||
|
'enabled',
|
||||||
|
'format',
|
||||||
|
'formats',
|
||||||
|
'headquarters',
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, all_plugins=[]):
|
||||||
|
SearchQueryParser.__init__(self, locations=self.USABLE_LOCATIONS)
|
||||||
|
self.srs = set(all_plugins)
|
||||||
|
|
||||||
|
def universal_set(self):
|
||||||
|
return self.srs
|
||||||
|
|
||||||
|
def get_matches(self, location, query):
|
||||||
|
location = location.lower().strip()
|
||||||
|
if location == 'formats':
|
||||||
|
location = 'format'
|
||||||
|
|
||||||
|
matchkind = CONTAINS_MATCH
|
||||||
|
if len(query) > 1:
|
||||||
|
if query.startswith('\\'):
|
||||||
|
query = query[1:]
|
||||||
|
elif query.startswith('='):
|
||||||
|
matchkind = EQUALS_MATCH
|
||||||
|
query = query[1:]
|
||||||
|
elif query.startswith('~'):
|
||||||
|
matchkind = REGEXP_MATCH
|
||||||
|
query = query[1:]
|
||||||
|
if matchkind != REGEXP_MATCH: ### leave case in regexps because it can be significant e.g. \S \W \D
|
||||||
|
query = query.lower()
|
||||||
|
|
||||||
|
if location not in self.USABLE_LOCATIONS:
|
||||||
|
return set([])
|
||||||
|
matches = set([])
|
||||||
|
all_locs = set(self.USABLE_LOCATIONS) - set(['all'])
|
||||||
|
locations = all_locs if location == 'all' else [location]
|
||||||
|
q = {
|
||||||
|
'description': lambda x: x.description.lower(),
|
||||||
|
'drm': lambda x: not x.drm_free_only,
|
||||||
|
'enabled': lambda x: not is_disabled(x),
|
||||||
|
'format': lambda x: ','.join(x.formats).lower(),
|
||||||
|
'headquarters': lambda x: x.headquarters.lower(),
|
||||||
|
'name': lambda x : x.name.lower(),
|
||||||
|
}
|
||||||
|
q['formats'] = q['format']
|
||||||
|
for sr in self.srs:
|
||||||
|
for locvalue in locations:
|
||||||
|
accessor = q[locvalue]
|
||||||
|
if query == 'true':
|
||||||
|
if locvalue in ('drm', 'enabled'):
|
||||||
|
if accessor(sr) == True:
|
||||||
|
matches.add(sr)
|
||||||
|
elif accessor(sr) is not None:
|
||||||
|
matches.add(sr)
|
||||||
|
continue
|
||||||
|
if query == 'false':
|
||||||
|
if locvalue in ('drm', 'enabled'):
|
||||||
|
if accessor(sr) == False:
|
||||||
|
matches.add(sr)
|
||||||
|
elif accessor(sr) is None:
|
||||||
|
matches.add(sr)
|
||||||
|
continue
|
||||||
|
# this is bool, so can't match below
|
||||||
|
if locvalue in ('drm', 'enabled'):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
### Can't separate authors because comma is used for name sep and author sep
|
||||||
|
### Exact match might not get what you want. For that reason, turn author
|
||||||
|
### exactmatch searches into contains searches.
|
||||||
|
if locvalue == 'name' and matchkind == EQUALS_MATCH:
|
||||||
|
m = CONTAINS_MATCH
|
||||||
|
else:
|
||||||
|
m = matchkind
|
||||||
|
|
||||||
|
if locvalue == 'format':
|
||||||
|
vals = accessor(sr).split(',')
|
||||||
|
else:
|
||||||
|
vals = [accessor(sr)]
|
||||||
|
if _match(query, vals, m):
|
||||||
|
matches.add(sr)
|
||||||
|
break
|
||||||
|
except ValueError: # Unicode errors
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
34
src/calibre/gui2/store/config/chooser/results_view.py
Normal file
34
src/calibre/gui2/store/config/chooser/results_view.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import (Qt, QTreeView, QSize)
|
||||||
|
|
||||||
|
from calibre.customize.ui import store_plugins
|
||||||
|
from calibre.gui2.metadata.single_download import RichTextDelegate
|
||||||
|
from calibre.gui2.store.config.chooser.models import Matches
|
||||||
|
|
||||||
|
class ResultsView(QTreeView):
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
QTreeView.__init__(self,*args)
|
||||||
|
|
||||||
|
self._model = Matches([p for p in store_plugins()])
|
||||||
|
self.setModel(self._model)
|
||||||
|
|
||||||
|
self.setIconSize(QSize(24, 24))
|
||||||
|
|
||||||
|
self.rt_delegate = RichTextDelegate(self)
|
||||||
|
|
||||||
|
for i in self._model.HTML_COLS:
|
||||||
|
self.setItemDelegateForColumn(i, self.rt_delegate)
|
||||||
|
|
||||||
|
for i in xrange(self._model.columnCount()):
|
||||||
|
self.resizeColumnToContents(i)
|
||||||
|
|
||||||
|
self.model().sort(1, Qt.AscendingOrder)
|
||||||
|
self.header().setSortIndicator(self.model().sort_col, self.model().sort_order)
|
0
src/calibre/gui2/store/config/search/__init__.py
Normal file
0
src/calibre/gui2/store/config/search/__init__.py
Normal file
@ -9,25 +9,25 @@ __docformat__ = 'restructuredtext en'
|
|||||||
from PyQt4.Qt import QWidget
|
from PyQt4.Qt import QWidget
|
||||||
|
|
||||||
from calibre.gui2 import JSONConfig
|
from calibre.gui2 import JSONConfig
|
||||||
from calibre.gui2.store.config.search_widget_ui import Ui_Form
|
from calibre.gui2.store.config.search.search_widget_ui import Ui_Form
|
||||||
|
|
||||||
class StoreConfigWidget(QWidget, Ui_Form):
|
class StoreConfigWidget(QWidget, Ui_Form):
|
||||||
|
|
||||||
def __init__(self, config=None):
|
def __init__(self, config=None):
|
||||||
QWidget.__init__(self)
|
QWidget.__init__(self)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
self.config = JSONConfig('store/search') if not config else config
|
self.config = JSONConfig('store/search') if not config else config
|
||||||
|
|
||||||
# These default values should be the same as in
|
# These default values should be the same as in
|
||||||
# calibre.gui2.store.search.search:SearchDialog.load_settings
|
# calibre.gui2.store.search.search:SearchDialog.load_settings
|
||||||
# Seconds
|
# Seconds
|
||||||
self.opt_timeout.setValue(self.config.get('timeout', 75))
|
self.opt_timeout.setValue(self.config.get('timeout', 75))
|
||||||
self.opt_hang_time.setValue(self.config.get('hang_time', 75))
|
self.opt_hang_time.setValue(self.config.get('hang_time', 75))
|
||||||
|
|
||||||
self.opt_max_results.setValue(self.config.get('max_results', 10))
|
self.opt_max_results.setValue(self.config.get('max_results', 10))
|
||||||
self.opt_open_external.setChecked(self.config.get('open_external', True))
|
self.opt_open_external.setChecked(self.config.get('open_external', True))
|
||||||
|
|
||||||
# Number of threads to run for each type of operation
|
# Number of threads to run for each type of operation
|
||||||
self.opt_search_thread_count.setValue(self.config.get('search_thread_count', 4))
|
self.opt_search_thread_count.setValue(self.config.get('search_thread_count', 4))
|
||||||
self.opt_cache_thread_count.setValue(self.config.get('cache_thread_count', 2))
|
self.opt_cache_thread_count.setValue(self.config.get('cache_thread_count', 2))
|
@ -93,13 +93,13 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox_3">
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Performance</string>
|
<string>Threads</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout_3">
|
<layout class="QFormLayout" name="formLayout_3">
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Number of simultaneous searches</string>
|
<string>Number of search threads to use</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -113,7 +113,7 @@
|
|||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Number of simultaneous cache updates</string>
|
<string>Number of cache update threads to use</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -127,7 +127,7 @@
|
|||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Number of simultaneous cover downloads</string>
|
<string>Number of conver download threads to use</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -141,7 +141,7 @@
|
|||||||
<item row="3" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_7">
|
<widget class="QLabel" name="label_7">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Number of simultaneous details downloads</string>
|
<string>Number of details threads to use</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
@ -11,7 +11,7 @@ Config widget access functions for configuring the store action.
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
def config_widget():
|
def config_widget():
|
||||||
from calibre.gui2.store.config.search_widget import StoreConfigWidget
|
from calibre.gui2.store.config.search.search_widget import StoreConfigWidget
|
||||||
return StoreConfigWidget()
|
return StoreConfigWidget()
|
||||||
|
|
||||||
def save_settings(config_widget):
|
def save_settings(config_widget):
|
||||||
|
@ -3,3 +3,6 @@ or asked not to be included in the store integration.
|
|||||||
|
|
||||||
* Borders (http://www.borders.com/)
|
* Borders (http://www.borders.com/)
|
||||||
* WH Smith (http://www.whsmith.co.uk/)
|
* WH Smith (http://www.whsmith.co.uk/)
|
||||||
|
Refused to permit signing up for the affiliate program
|
||||||
|
* Libraria Rizzoli (http://libreriarizzoli.corriere.it/).
|
||||||
|
No reply with two attempts over 2 weeks
|
75
src/calibre/gui2/store/legimi_plugin.py
Normal file
75
src/calibre/gui2/store/legimi_plugin.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import re
|
||||||
|
import urllib
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
from lxml import html
|
||||||
|
|
||||||
|
from PyQt4.Qt import QUrl
|
||||||
|
|
||||||
|
from calibre import browser, url_slash_cleaner
|
||||||
|
from calibre.gui2 import open_url
|
||||||
|
from calibre.gui2.store import StorePlugin
|
||||||
|
from calibre.gui2.store.basic_config import BasicStoreConfig
|
||||||
|
from calibre.gui2.store.search_result import SearchResult
|
||||||
|
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||||
|
|
||||||
|
class LegimiStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
|
|
||||||
|
url = 'http://www.legimi.com/pl/ebooks/?price=any'
|
||||||
|
detail_url = None
|
||||||
|
|
||||||
|
if detail_item:
|
||||||
|
detail_url = detail_item
|
||||||
|
|
||||||
|
if external or self.config.get('open_external', False):
|
||||||
|
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
||||||
|
else:
|
||||||
|
d = WebStoreDialog(self.gui, url, parent, detail_url)
|
||||||
|
d.setWindowTitle(self.name)
|
||||||
|
d.set_tags(self.config.get('tags', ''))
|
||||||
|
d.exec_()
|
||||||
|
|
||||||
|
def search(self, query, max_results=10, timeout=60):
|
||||||
|
url = 'http://www.legimi.com/pl/ebooks/?price=any&lang=pl&search=' + urllib.quote_plus(query.encode('utf-8')) + '&sort=relevance'
|
||||||
|
|
||||||
|
br = browser()
|
||||||
|
|
||||||
|
counter = max_results
|
||||||
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
|
doc = html.fromstring(f.read())
|
||||||
|
for data in doc.xpath('//div[@class="list"]/ul/li'):
|
||||||
|
if counter <= 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
id = ''.join(data.xpath('.//div[@class="item_cover_container"]/a[1]/@href'))
|
||||||
|
if not id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
cover_url = ''.join(data.xpath('.//div[@class="item_cover_container"]/a/img/@src'))
|
||||||
|
title = ''.join(data.xpath('.//div[@class="item_entries"]/h2/a/text()'))
|
||||||
|
author = ''.join(data.xpath('.//div[@class="item_entries"]/span[1]/a/text()'))
|
||||||
|
price = ''.join(data.xpath('.//div[@class="item_entries"]/span[3]/text()'))
|
||||||
|
price = re.sub(r'[^0-9,]*','',price) + ' zł'
|
||||||
|
|
||||||
|
counter -= 1
|
||||||
|
|
||||||
|
s = SearchResult()
|
||||||
|
s.cover_url = 'http://www.legimi.com/' + cover_url
|
||||||
|
s.title = title.strip()
|
||||||
|
s.author = author.strip()
|
||||||
|
s.price = price
|
||||||
|
s.detail_item = 'http://www.legimi.com/' + id.strip()
|
||||||
|
s.drm = SearchResult.DRM_LOCKED
|
||||||
|
s.formats = 'EPUB'
|
||||||
|
|
||||||
|
yield s
|
@ -47,6 +47,7 @@ class BooksModel(QAbstractItemModel):
|
|||||||
self.books = list(self.search_filter.parse(self.filter))
|
self.books = list(self.search_filter.parse(self.filter))
|
||||||
except:
|
except:
|
||||||
self.books = self.all_books
|
self.books = self.all_books
|
||||||
|
self.layoutChanged.emit()
|
||||||
self.sort(self.sort_col, self.sort_order)
|
self.sort(self.sort_col, self.sort_order)
|
||||||
self.total_changed.emit(self.rowCount())
|
self.total_changed.emit(self.rowCount())
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ class AdvSearchBuilderDialog(QDialog, Ui_Dialog):
|
|||||||
if price:
|
if price:
|
||||||
ans.append('price:"' + self.mc + price + '"')
|
ans.append('price:"' + self.mc + price + '"')
|
||||||
format = unicode(self.format_box.text()).strip()
|
format = unicode(self.format_box.text()).strip()
|
||||||
if author:
|
if format:
|
||||||
ans.append('format:"' + self.mc + format + '"')
|
ans.append('format:"' + self.mc + format + '"')
|
||||||
if ans:
|
if ans:
|
||||||
return ' and '.join(ans)
|
return ' and '.join(ans)
|
||||||
|
@ -14,7 +14,7 @@ from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox,
|
|||||||
|
|
||||||
from calibre.gui2 import JSONConfig, info_dialog
|
from calibre.gui2 import JSONConfig, info_dialog
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
from calibre.gui2.store.config.search_widget import StoreConfigWidget
|
from calibre.gui2.store.config.search.search_widget import StoreConfigWidget
|
||||||
from calibre.gui2.store.search.adv_search_builder import AdvSearchBuilderDialog
|
from calibre.gui2.store.search.adv_search_builder import AdvSearchBuilderDialog
|
||||||
from calibre.gui2.store.search.download_thread import SearchThreadPool, \
|
from calibre.gui2.store.search.download_thread import SearchThreadPool, \
|
||||||
CacheUpdateThreadPool
|
CacheUpdateThreadPool
|
||||||
@ -238,7 +238,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
v = QVBoxLayout(d)
|
v = QVBoxLayout(d)
|
||||||
button_box.accepted.connect(d.accept)
|
button_box.accepted.connect(d.accept)
|
||||||
button_box.rejected.connect(d.reject)
|
button_box.rejected.connect(d.reject)
|
||||||
d.setWindowTitle(_('Customize Get Books'))
|
d.setWindowTitle(_('Customize get books search'))
|
||||||
config_widget = StoreConfigWidget(self.config)
|
config_widget = StoreConfigWidget(self.config)
|
||||||
v.addWidget(config_widget)
|
v.addWidget(config_widget)
|
||||||
v.addWidget(button_box)
|
v.addWidget(button_box)
|
||||||
|
@ -82,8 +82,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>102</width>
|
<width>125</width>
|
||||||
<height>129</height>
|
<height>127</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -159,6 +159,9 @@
|
|||||||
<property name="expandsOnDoubleClick">
|
<property name="expandsOnDoubleClick">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<attribute name="headerStretchLastSection">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user