mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	Sync to trunk.
This commit is contained in:
		
						commit
						ae3aecc69c
					
				@ -31,3 +31,4 @@ nbproject/
 | 
			
		||||
.pydevproject
 | 
			
		||||
.settings/
 | 
			
		||||
*.DS_Store
 | 
			
		||||
calibre_plugins/
 | 
			
		||||
							
								
								
									
										55
									
								
								recipes/dilemaveche.recipe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								recipes/dilemaveche.recipe
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
#!/usr/bin/env  python
 | 
			
		||||
 | 
			
		||||
__license__   = 'GPL v3'
 | 
			
		||||
__copyright__ = u'2011, Silviu Cotoar\u0103'
 | 
			
		||||
'''
 | 
			
		||||
dilemaveche.ro
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
from calibre.web.feeds.news import BasicNewsRecipe
 | 
			
		||||
 | 
			
		||||
class DilemaVeche(BasicNewsRecipe):
 | 
			
		||||
    title                 = u'Dilema Veche'
 | 
			
		||||
    __author__            = u'Silviu Cotoar\u0103'
 | 
			
		||||
    description           = u'Sunt vechi, domnule!'
 | 
			
		||||
    publisher             = u'Dilema Veche'
 | 
			
		||||
    oldest_article        = 50
 | 
			
		||||
    language              = 'ro'
 | 
			
		||||
    max_articles_per_feed = 100
 | 
			
		||||
    no_stylesheets        = True
 | 
			
		||||
    use_embedded_content  = False
 | 
			
		||||
    category              = 'Ziare'
 | 
			
		||||
    encoding              = 'utf-8'
 | 
			
		||||
    cover_url             = 'http://www.dilemaveche.ro/sites/all/themes/dilema/theme/dilema_two/layouter/dilema_two_homepage/logo.png'
 | 
			
		||||
 | 
			
		||||
    conversion_options = {
 | 
			
		||||
                             'comments'   : description
 | 
			
		||||
                            ,'tags'       : category
 | 
			
		||||
                            ,'language'   : language
 | 
			
		||||
                            ,'publisher'  : publisher
 | 
			
		||||
                         }
 | 
			
		||||
 | 
			
		||||
    keep_only_tags = [
 | 
			
		||||
                          dict(name='h1', attrs={'class':'art_title'})
 | 
			
		||||
                        , dict(name='h1', attrs={'class':'art_title online'})
 | 
			
		||||
                        , dict(name='div', attrs={'class':'item'})
 | 
			
		||||
                        , dict(name='div', attrs={'class':'art_content'})
 | 
			
		||||
                     ]
 | 
			
		||||
 | 
			
		||||
    remove_tags = [
 | 
			
		||||
                         dict(name='div', attrs={'class':['article_details']})
 | 
			
		||||
                        , dict(name='div', attrs={'class':['controale']})
 | 
			
		||||
                        , dict(name='div', attrs={'class':['art_related_left']})
 | 
			
		||||
                  ]
 | 
			
		||||
 | 
			
		||||
    remove_tags_after = [
 | 
			
		||||
                            dict(name='div', attrs={'class':['article_details']})
 | 
			
		||||
                        ]
 | 
			
		||||
 | 
			
		||||
    feeds          = [
 | 
			
		||||
                        (u'Feeds', u'http://www.dilemaveche.ro/rss.xml')
 | 
			
		||||
                     ]
 | 
			
		||||
 | 
			
		||||
    def preprocess_html(self, soup):
 | 
			
		||||
        return self.adeify_images(soup)
 | 
			
		||||
@ -6,13 +6,13 @@ __copyright__ = 'Copyright 2010 Starson17'
 | 
			
		||||
www.gocomics.com
 | 
			
		||||
'''
 | 
			
		||||
from calibre.web.feeds.news import BasicNewsRecipe
 | 
			
		||||
import mechanize
 | 
			
		||||
import mechanize, re
 | 
			
		||||
 | 
			
		||||
class GoComics(BasicNewsRecipe):
 | 
			
		||||
    title               = 'GoComics'
 | 
			
		||||
    __author__          = 'Starson17'
 | 
			
		||||
    __version__         = '1.03'
 | 
			
		||||
    __date__            = '09 October 2010'
 | 
			
		||||
    __version__         = '1.05'
 | 
			
		||||
    __date__            = '19 may 2011'
 | 
			
		||||
    description         = u'200+ Comics - Customize for more days/comics: Defaults to 7 days, 25 comics - 20 general, 5 editorial.'
 | 
			
		||||
    category            = 'news, comics'
 | 
			
		||||
    language            = 'en'
 | 
			
		||||
@ -20,6 +20,7 @@ class GoComics(BasicNewsRecipe):
 | 
			
		||||
    no_stylesheets      = True
 | 
			
		||||
    remove_javascript   = True
 | 
			
		||||
    cover_url           = 'http://paulbuckley14059.files.wordpress.com/2008/06/calvin-and-hobbes.jpg'
 | 
			
		||||
    remove_attributes = ['style']
 | 
			
		||||
 | 
			
		||||
    ####### USER PREFERENCES - COMICS, IMAGE SIZE AND NUMBER OF COMICS TO RETRIEVE ########
 | 
			
		||||
    # num_comics_to_get - I've tried up to 99 on Calvin&Hobbes
 | 
			
		||||
@ -40,6 +41,8 @@ class GoComics(BasicNewsRecipe):
 | 
			
		||||
 | 
			
		||||
    remove_tags = [dict(name='a', attrs={'class':['beginning','prev','cal','next','newest']}),
 | 
			
		||||
                   dict(name='div', attrs={'class':['tag-wrapper']}),
 | 
			
		||||
                   dict(name='a', attrs={'href':re.compile(r'.*mutable_[0-9]+', re.IGNORECASE)}),
 | 
			
		||||
                   dict(name='img', attrs={'src':re.compile(r'.*mutable_[0-9]+', re.IGNORECASE)}),
 | 
			
		||||
                   dict(name='ul', attrs={'class':['share-nav','feature-nav']}),
 | 
			
		||||
                   ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								recipes/icons/dilemaveche.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								recipes/icons/dilemaveche.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 558 B  | 
							
								
								
									
										
											BIN
										
									
								
								recipes/icons/natgeo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								recipes/icons/natgeo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 247 B  | 
@ -3,8 +3,9 @@ from calibre.web.feeds.news import BasicNewsRecipe
 | 
			
		||||
class AdvancedUserRecipe1295262156(BasicNewsRecipe):
 | 
			
		||||
    title          = u'kath.net'
 | 
			
		||||
    __author__     = 'Bobus'
 | 
			
		||||
    description    = u'Katholische Nachrichten'
 | 
			
		||||
    oldest_article = 7
 | 
			
		||||
    language = 'en'
 | 
			
		||||
    language = 'de'
 | 
			
		||||
    max_articles_per_feed = 100
 | 
			
		||||
 | 
			
		||||
    feeds          = [(u'kath.net', u'http://www.kath.net/2005/xml/index.xml')]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								recipes/natgeo.recipe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								recipes/natgeo.recipe
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
#!/usr/bin/env  python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
__license__   = 'GPL v3'
 | 
			
		||||
__copyright__ = '2011, gagsays <gagsays at gmail dot com>'
 | 
			
		||||
'''
 | 
			
		||||
nationalgeographic.com
 | 
			
		||||
'''
 | 
			
		||||
from calibre.web.feeds.news import BasicNewsRecipe
 | 
			
		||||
class NatGeo(BasicNewsRecipe):
 | 
			
		||||
    title          = u'National Geographic'
 | 
			
		||||
    description = 'Daily news articles from The National Geographic'
 | 
			
		||||
    language = 'en'
 | 
			
		||||
    oldest_article = 20
 | 
			
		||||
    max_articles_per_feed = 25
 | 
			
		||||
    encoding              = 'utf8'
 | 
			
		||||
    publisher              = 'nationalgeographic.com'
 | 
			
		||||
    category               = 'science, nat geo'
 | 
			
		||||
    __author__           = 'gagsays'
 | 
			
		||||
    masthead_url        = 'http://s.ngeo.com/wpf/sites/themes/global/i/presentation/ng_logo_small.png'
 | 
			
		||||
    description           = 'Inspiring people to care about the planet since 1888'
 | 
			
		||||
    timefmt = ' [%a, %d %b, %Y]'
 | 
			
		||||
    no_stylesheets        = True
 | 
			
		||||
    use_embedded_content  = False
 | 
			
		||||
 | 
			
		||||
    extra_css = '''
 | 
			
		||||
                body {color: #000000;font-size: medium;}
 | 
			
		||||
                h1 {color: #222222; font-size: large; font-weight:lighter; text-decoration:none; text-align: center;font-family:Georgia,Times New Roman,Times,serif;}
 | 
			
		||||
          h2 {color: #454545; font-size: small; font-weight:lighter; text-decoration:none; text-align: justify; font-style:italic;font-family :Georgia,Times New Roman,Times,serif;}
 | 
			
		||||
                h3 {color: #555555; font-size: small; font-style:italic; margin-top: 10px;}
 | 
			
		||||
                img{margin-bottom: 0.25em;display:block;margin-left: auto;margin-right: auto;}
 | 
			
		||||
                a:link,a,.a,href {text-decoration: none;color: #000000;}
 | 
			
		||||
                .caption{color: #000000;font-size: xx-small;text-align: justify;font-weight:normal;}
 | 
			
		||||
                .credit{color: #555555;font-size: xx-small;text-align: left;font-weight:lighter;}
 | 
			
		||||
                p.author,p.publication{color: #000000;font-size: xx-small;text-align: left;display:inline;}
 | 
			
		||||
                p.publication_time{color: #000000;font-size: xx-small;text-align: right;text-decoration: underline;}
 | 
			
		||||
                p {margin-bottom: 0;}
 | 
			
		||||
                p + p {text-indent: 1.5em;margin-top: 0;}
 | 
			
		||||
                .hidden{display:none;}
 | 
			
		||||
                #page_head{text-transform:uppercase;}
 | 
			
		||||
               '''
 | 
			
		||||
 | 
			
		||||
    def parse_feeds (self):
 | 
			
		||||
      feeds = BasicNewsRecipe.parse_feeds(self)
 | 
			
		||||
      for feed in feeds:
 | 
			
		||||
        for article in feed.articles[:]:
 | 
			
		||||
          if 'Presented' in article.title or 'Pictures' in article.title:
 | 
			
		||||
            feed.articles.remove(article)
 | 
			
		||||
      return feeds
 | 
			
		||||
 | 
			
		||||
    def preprocess_html(self, soup):
 | 
			
		||||
        for alink in soup.findAll('a'):
 | 
			
		||||
            if alink.string is not None:
 | 
			
		||||
               tstr = alink.string
 | 
			
		||||
               alink.replaceWith(tstr)
 | 
			
		||||
        return soup
 | 
			
		||||
 | 
			
		||||
    remove_tags_before = dict(id='page_head')
 | 
			
		||||
    keep_only_tags = [
 | 
			
		||||
         dict(name='div',attrs={'id':['page_head','content_mainA']})
 | 
			
		||||
    ]
 | 
			
		||||
    remove_tags_after = [
 | 
			
		||||
         dict(name='div',attrs={'class':['article_text','promo_collection']})
 | 
			
		||||
    ]
 | 
			
		||||
    remove_tags    = [
 | 
			
		||||
                               dict(name='div', attrs={'class':['aside','primary full_width']})
 | 
			
		||||
                               ,dict(name='div', attrs={'id':['header_search','navigation_mainB_wrap']})
 | 
			
		||||
                                ]
 | 
			
		||||
    feeds = [
 | 
			
		||||
                    (u'Daily News', u'http://feeds.nationalgeographic.com/ng/News/News_Main')
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
@ -941,7 +941,7 @@ class ITUNES(DriverBase):
 | 
			
		||||
        # declared in use_plugboard_ext and a device name of ITUNES
 | 
			
		||||
        if DEBUG:
 | 
			
		||||
            self.log.info("ITUNES.set_plugboard()")
 | 
			
		||||
            #self.log.info('  using plugboard %s' % plugboards)
 | 
			
		||||
            #self.log.info('  plugboard: %s' % plugboards)
 | 
			
		||||
        self.plugboards = plugboards
 | 
			
		||||
        self.plugboard_func = pb_func
 | 
			
		||||
 | 
			
		||||
@ -1052,7 +1052,6 @@ class ITUNES(DriverBase):
 | 
			
		||||
                    'title': metadata[i].title,
 | 
			
		||||
                     'uuid': metadata[i].uuid }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                # Report progress
 | 
			
		||||
                if self.report_progress is not None:
 | 
			
		||||
                    self.report_progress((i+1)/file_count, _('%d of %d') % (i+1, file_count))
 | 
			
		||||
@ -2744,7 +2743,7 @@ class ITUNES(DriverBase):
 | 
			
		||||
        # Update metadata from plugboard
 | 
			
		||||
        # If self.plugboard is None (no transforms), original metadata is returned intact
 | 
			
		||||
        metadata_x = self._xform_metadata_via_plugboard(metadata, this_book.format)
 | 
			
		||||
 | 
			
		||||
        self.log("metadata.title_sort: %s  metadata_x.title_sort: %s" % (metadata.title_sort, metadata_x.title_sort))
 | 
			
		||||
        if isosx:
 | 
			
		||||
            if lb_added:
 | 
			
		||||
                lb_added.name.set(metadata_x.title)
 | 
			
		||||
@ -2754,8 +2753,7 @@ class ITUNES(DriverBase):
 | 
			
		||||
                lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
 | 
			
		||||
                lb_added.enabled.set(True)
 | 
			
		||||
                lb_added.sort_artist.set(icu_title(metadata_x.author_sort))
 | 
			
		||||
                lb_added.sort_name.set(metadata.title_sort)
 | 
			
		||||
 | 
			
		||||
                lb_added.sort_name.set(metadata_x.title_sort)
 | 
			
		||||
 | 
			
		||||
            if db_added:
 | 
			
		||||
                db_added.name.set(metadata_x.title)
 | 
			
		||||
@ -2765,7 +2763,7 @@ class ITUNES(DriverBase):
 | 
			
		||||
                db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
 | 
			
		||||
                db_added.enabled.set(True)
 | 
			
		||||
                db_added.sort_artist.set(icu_title(metadata_x.author_sort))
 | 
			
		||||
                db_added.sort_name.set(metadata.title_sort)
 | 
			
		||||
                db_added.sort_name.set(metadata_x.title_sort)
 | 
			
		||||
 | 
			
		||||
            if metadata_x.comments:
 | 
			
		||||
                if lb_added:
 | 
			
		||||
@ -2785,6 +2783,7 @@ class ITUNES(DriverBase):
 | 
			
		||||
 | 
			
		||||
            # Set genre from series if available, else first alpha tag
 | 
			
		||||
            # Otherwise iTunes grabs the first dc:subject from the opf metadata
 | 
			
		||||
            # If title_sort applied in plugboard, that overrides using series/index as title_sort
 | 
			
		||||
            if metadata_x.series and self.settings().extra_customization[self.USE_SERIES_AS_CATEGORY]:
 | 
			
		||||
                if DEBUG:
 | 
			
		||||
                    self.log.info(" ITUNES._update_iTunes_metadata()")
 | 
			
		||||
@ -2796,7 +2795,9 @@ class ITUNES(DriverBase):
 | 
			
		||||
                fraction = index-integer
 | 
			
		||||
                series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
 | 
			
		||||
                if lb_added:
 | 
			
		||||
                    lb_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index))
 | 
			
		||||
                    # If no title_sort plugboard tweak, create sort_name from series/index
 | 
			
		||||
                    if metadata.title_sort == metadata_x.title_sort:
 | 
			
		||||
                        lb_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index))
 | 
			
		||||
                    lb_added.episode_ID.set(metadata_x.series)
 | 
			
		||||
                    lb_added.episode_number.set(metadata_x.series_index)
 | 
			
		||||
 | 
			
		||||
@ -2810,7 +2811,9 @@ class ITUNES(DriverBase):
 | 
			
		||||
                                break
 | 
			
		||||
 | 
			
		||||
                if db_added:
 | 
			
		||||
                    db_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index))
 | 
			
		||||
                    # If no title_sort plugboard tweak, create sort_name from series/index
 | 
			
		||||
                    if metadata.title_sort == metadata_x.title_sort:
 | 
			
		||||
                        db_added.sort_name.set("%s %s" % (self.title_sorter(metadata_x.series), series_index))
 | 
			
		||||
                    db_added.episode_ID.set(metadata_x.series)
 | 
			
		||||
                    db_added.episode_number.set(metadata_x.series_index)
 | 
			
		||||
 | 
			
		||||
@ -2845,7 +2848,7 @@ class ITUNES(DriverBase):
 | 
			
		||||
                lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
 | 
			
		||||
                lb_added.Enabled = True
 | 
			
		||||
                lb_added.SortArtist = icu_title(metadata_x.author_sort)
 | 
			
		||||
                lb_added.SortName = metadata.title_sort
 | 
			
		||||
                lb_added.SortName = metadata_x.title_sort
 | 
			
		||||
 | 
			
		||||
            if db_added:
 | 
			
		||||
                db_added.Name = metadata_x.title
 | 
			
		||||
@ -2855,7 +2858,7 @@ class ITUNES(DriverBase):
 | 
			
		||||
                db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
 | 
			
		||||
                db_added.Enabled = True
 | 
			
		||||
                db_added.SortArtist = icu_title(metadata_x.author_sort)
 | 
			
		||||
                db_added.SortName = metadata.title_sort
 | 
			
		||||
                db_added.SortName = metadata_x.title_sort
 | 
			
		||||
 | 
			
		||||
            if metadata_x.comments:
 | 
			
		||||
                if lb_added:
 | 
			
		||||
@ -2888,7 +2891,9 @@ class ITUNES(DriverBase):
 | 
			
		||||
                fraction = index-integer
 | 
			
		||||
                series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0'))
 | 
			
		||||
                if lb_added:
 | 
			
		||||
                    lb_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index)
 | 
			
		||||
                    # If no title_sort plugboard tweak, create sort_name from series/index
 | 
			
		||||
                    if metadata.title_sort == metadata_x.title_sort:
 | 
			
		||||
                        lb_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index)
 | 
			
		||||
                    lb_added.EpisodeID = metadata_x.series
 | 
			
		||||
 | 
			
		||||
                    try:
 | 
			
		||||
@ -2914,7 +2919,9 @@ class ITUNES(DriverBase):
 | 
			
		||||
                                break
 | 
			
		||||
 | 
			
		||||
                if db_added:
 | 
			
		||||
                    db_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index)
 | 
			
		||||
                    # If no title_sort plugboard tweak, create sort_name from series/index
 | 
			
		||||
                    if metadata.title_sort == metadata_x.title_sort:
 | 
			
		||||
                        db_added.SortName = "%s %s" % (self.title_sorter(metadata_x.series), series_index)
 | 
			
		||||
                    db_added.EpisodeID = metadata_x.series
 | 
			
		||||
 | 
			
		||||
                    try:
 | 
			
		||||
@ -2975,6 +2982,9 @@ class ITUNES(DriverBase):
 | 
			
		||||
                                           newmi.publisher if book.publisher != newmi.publisher else ''))
 | 
			
		||||
                self.log.info("        tags: %s %s" % (book.tags, ">>> %s" %
 | 
			
		||||
                                           newmi.tags if book.tags != newmi.tags else ''))
 | 
			
		||||
            else:
 | 
			
		||||
                self.log("  matching plugboard not found")
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            newmi = book
 | 
			
		||||
        return newmi
 | 
			
		||||
 | 
			
		||||
@ -9,11 +9,12 @@ from functools import partial
 | 
			
		||||
 | 
			
		||||
from PyQt4.Qt import QMenu, QObject, QTimer
 | 
			
		||||
 | 
			
		||||
from calibre.gui2 import error_dialog
 | 
			
		||||
from calibre.gui2 import error_dialog, question_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
 | 
			
		||||
from calibre.utils.recycle_bin import can_recycle
 | 
			
		||||
 | 
			
		||||
single_shot = partial(QTimer.singleShot, 10)
 | 
			
		||||
 | 
			
		||||
@ -24,6 +25,15 @@ class MultiDeleter(QObject):
 | 
			
		||||
        QObject.__init__(self, gui)
 | 
			
		||||
        self.model = gui.library_view.model()
 | 
			
		||||
        self.ids = ids
 | 
			
		||||
        self.permanent = False
 | 
			
		||||
        if can_recycle and len(ids) > 100:
 | 
			
		||||
            if question_dialog(gui, _('Are you sure?'), '<p>'+
 | 
			
		||||
                _('You are trying to delete %d books. '
 | 
			
		||||
                    'Sending so many files to the Recycle'
 | 
			
		||||
                    ' Bin <b>can be slow</b>. Should calibre skip the'
 | 
			
		||||
                    ' Recycle Bin? If you click Yes the files'
 | 
			
		||||
                    ' will be <b>permanently deleted</b>.')%len(ids)):
 | 
			
		||||
                self.permanent = True
 | 
			
		||||
        self.gui = gui
 | 
			
		||||
        self.failures = []
 | 
			
		||||
        self.deleted_ids = []
 | 
			
		||||
@ -44,7 +54,8 @@ class MultiDeleter(QObject):
 | 
			
		||||
            title_ = self.model.db.title(id_, index_is_id=True)
 | 
			
		||||
            if title_:
 | 
			
		||||
                title = title_
 | 
			
		||||
            self.model.db.delete_book(id_, notify=False, commit=False)
 | 
			
		||||
            self.model.db.delete_book(id_, notify=False, commit=False,
 | 
			
		||||
                    permanent=self.permanent)
 | 
			
		||||
            self.deleted_ids.append(id_)
 | 
			
		||||
        except:
 | 
			
		||||
            import traceback
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										316
									
								
								src/calibre/gui2/bars.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								src/calibre/gui2/bars.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,316 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
 | 
			
		||||
from __future__ import (unicode_literals, division, absolute_import,
 | 
			
		||||
                        print_function)
 | 
			
		||||
 | 
			
		||||
__license__   = 'GPL v3'
 | 
			
		||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
 | 
			
		||||
__docformat__ = 'restructuredtext en'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from PyQt4.Qt import (QObject, QToolBar, Qt, QSize, QToolButton, QVBoxLayout,
 | 
			
		||||
        QLabel, QWidget, QAction, QMenuBar, QMenu)
 | 
			
		||||
 | 
			
		||||
from calibre.constants import isosx
 | 
			
		||||
from calibre.gui2 import gprefs
 | 
			
		||||
 | 
			
		||||
class ToolBar(QToolBar): # {{{
 | 
			
		||||
 | 
			
		||||
    def __init__(self, donate, location_manager, parent):
 | 
			
		||||
        QToolBar.__init__(self, parent)
 | 
			
		||||
        self.setContextMenuPolicy(Qt.PreventContextMenu)
 | 
			
		||||
        self.setMovable(False)
 | 
			
		||||
        self.setFloatable(False)
 | 
			
		||||
        self.setOrientation(Qt.Horizontal)
 | 
			
		||||
        self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
 | 
			
		||||
        self.setStyleSheet('QToolButton:checked { font-weight: bold }')
 | 
			
		||||
        self.preferred_width = self.sizeHint().width()
 | 
			
		||||
        self.gui = parent
 | 
			
		||||
        self.donate_button = donate
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
 | 
			
		||||
        self.location_manager = location_manager
 | 
			
		||||
        donate.setAutoRaise(True)
 | 
			
		||||
        donate.setCursor(Qt.PointingHandCursor)
 | 
			
		||||
        self.setAcceptDrops(True)
 | 
			
		||||
        self.showing_donate = False
 | 
			
		||||
 | 
			
		||||
    def resizeEvent(self, ev):
 | 
			
		||||
        QToolBar.resizeEvent(self, ev)
 | 
			
		||||
        style = self.get_text_style()
 | 
			
		||||
        self.setToolButtonStyle(style)
 | 
			
		||||
        if hasattr(self, 'd_widget') and hasattr(self.d_widget, 'filler'):
 | 
			
		||||
            self.d_widget.filler.setVisible(style != Qt.ToolButtonIconOnly)
 | 
			
		||||
 | 
			
		||||
    def get_text_style(self):
 | 
			
		||||
        style = Qt.ToolButtonTextUnderIcon
 | 
			
		||||
        s = gprefs['toolbar_icon_size']
 | 
			
		||||
        if s != 'off':
 | 
			
		||||
            p = gprefs['toolbar_text']
 | 
			
		||||
            if p == 'never':
 | 
			
		||||
                style = Qt.ToolButtonIconOnly
 | 
			
		||||
            elif p == 'auto' and self.preferred_width > self.width()+35:
 | 
			
		||||
                style = Qt.ToolButtonIconOnly
 | 
			
		||||
        return style
 | 
			
		||||
 | 
			
		||||
    def contextMenuEvent(self, *args):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def update_lm_actions(self):
 | 
			
		||||
        for ac in self.added_actions:
 | 
			
		||||
            if ac in self.location_manager.all_actions:
 | 
			
		||||
                ac.setVisible(ac in self.location_manager.available_actions)
 | 
			
		||||
 | 
			
		||||
    def init_bar(self, actions):
 | 
			
		||||
        self.showing_donate = False
 | 
			
		||||
        for ac in self.added_actions:
 | 
			
		||||
            m = ac.menu()
 | 
			
		||||
            if m is not None:
 | 
			
		||||
                m.setVisible(False)
 | 
			
		||||
 | 
			
		||||
        self.clear()
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
 | 
			
		||||
        bar = self
 | 
			
		||||
 | 
			
		||||
        for what in actions:
 | 
			
		||||
            if what is None:
 | 
			
		||||
                bar.addSeparator()
 | 
			
		||||
            elif what == 'Location Manager':
 | 
			
		||||
                for ac in self.location_manager.all_actions:
 | 
			
		||||
                    bar.addAction(ac)
 | 
			
		||||
                    bar.added_actions.append(ac)
 | 
			
		||||
                    bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup)
 | 
			
		||||
                    ac.setVisible(False)
 | 
			
		||||
            elif what == 'Donate':
 | 
			
		||||
                self.d_widget = QWidget()
 | 
			
		||||
                self.d_widget.setLayout(QVBoxLayout())
 | 
			
		||||
                self.d_widget.layout().addWidget(self.donate_button)
 | 
			
		||||
                if isosx:
 | 
			
		||||
                    self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
 | 
			
		||||
                    self.d_widget.layout().setContentsMargins(0,0,0,0)
 | 
			
		||||
                    self.d_widget.setContentsMargins(0,0,0,0)
 | 
			
		||||
                    self.d_widget.filler = QLabel(u'\u00a0')
 | 
			
		||||
                    self.d_widget.layout().addWidget(self.d_widget.filler)
 | 
			
		||||
                bar.addWidget(self.d_widget)
 | 
			
		||||
                self.showing_donate = True
 | 
			
		||||
            elif what in self.gui.iactions:
 | 
			
		||||
                action = self.gui.iactions[what]
 | 
			
		||||
                bar.addAction(action.qaction)
 | 
			
		||||
                self.added_actions.append(action.qaction)
 | 
			
		||||
                self.setup_tool_button(bar, action.qaction, action.popup_type)
 | 
			
		||||
        self.preferred_width = self.sizeHint().width()
 | 
			
		||||
 | 
			
		||||
    def setup_tool_button(self, bar, ac, menu_mode=None):
 | 
			
		||||
        ch = bar.widgetForAction(ac)
 | 
			
		||||
        if ch is None:
 | 
			
		||||
            ch = self.child_bar.widgetForAction(ac)
 | 
			
		||||
        ch.setCursor(Qt.PointingHandCursor)
 | 
			
		||||
        ch.setAutoRaise(True)
 | 
			
		||||
        if ac.menu() is not None and menu_mode is not None:
 | 
			
		||||
            ch.setPopupMode(menu_mode)
 | 
			
		||||
        return ch
 | 
			
		||||
 | 
			
		||||
    #support drag&drop from/to library from/to reader/card
 | 
			
		||||
    def dragEnterEvent(self, event):
 | 
			
		||||
        md = event.mimeData()
 | 
			
		||||
        if md.hasFormat("application/calibre+from_library") or \
 | 
			
		||||
           md.hasFormat("application/calibre+from_device"):
 | 
			
		||||
            event.setDropAction(Qt.CopyAction)
 | 
			
		||||
            event.accept()
 | 
			
		||||
        else:
 | 
			
		||||
            event.ignore()
 | 
			
		||||
 | 
			
		||||
    def dragMoveEvent(self, event):
 | 
			
		||||
        allowed = False
 | 
			
		||||
        md = event.mimeData()
 | 
			
		||||
        #Drop is only allowed in the location manager widget's different from the selected one
 | 
			
		||||
        for ac in self.location_manager.available_actions:
 | 
			
		||||
            w = self.widgetForAction(ac)
 | 
			
		||||
            if w is not None:
 | 
			
		||||
                if ( md.hasFormat("application/calibre+from_library") or \
 | 
			
		||||
                     md.hasFormat("application/calibre+from_device") ) and \
 | 
			
		||||
                        w.geometry().contains(event.pos()) and \
 | 
			
		||||
                        isinstance(w, QToolButton) and not w.isChecked():
 | 
			
		||||
                    allowed = True
 | 
			
		||||
                    break
 | 
			
		||||
        if allowed:
 | 
			
		||||
            event.acceptProposedAction()
 | 
			
		||||
        else:
 | 
			
		||||
            event.ignore()
 | 
			
		||||
 | 
			
		||||
    def dropEvent(self, event):
 | 
			
		||||
        data = event.mimeData()
 | 
			
		||||
 | 
			
		||||
        mime = 'application/calibre+from_library'
 | 
			
		||||
        if data.hasFormat(mime):
 | 
			
		||||
            ids = list(map(int, str(data.data(mime)).split()))
 | 
			
		||||
            tgt = None
 | 
			
		||||
            for ac in self.location_manager.available_actions:
 | 
			
		||||
                w = self.widgetForAction(ac)
 | 
			
		||||
                if w is not None and w.geometry().contains(event.pos()):
 | 
			
		||||
                    tgt = ac.calibre_name
 | 
			
		||||
            if tgt is not None:
 | 
			
		||||
                if tgt == 'main':
 | 
			
		||||
                    tgt = None
 | 
			
		||||
                self.gui.sync_to_device(tgt, False, send_ids=ids)
 | 
			
		||||
                event.accept()
 | 
			
		||||
 | 
			
		||||
        mime = 'application/calibre+from_device'
 | 
			
		||||
        if data.hasFormat(mime):
 | 
			
		||||
            paths = [unicode(u.toLocalFile()) for u in data.urls()]
 | 
			
		||||
            if paths:
 | 
			
		||||
                self.gui.iactions['Add Books'].add_books_from_device(
 | 
			
		||||
                        self.gui.current_view(), paths=paths)
 | 
			
		||||
                event.accept()
 | 
			
		||||
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class MenuAction(QAction): # {{{
 | 
			
		||||
 | 
			
		||||
    def __init__(self, clone, parent):
 | 
			
		||||
        QAction.__init__(self, clone.text(), parent)
 | 
			
		||||
        self.clone = clone
 | 
			
		||||
        clone.changed.connect(self.clone_changed)
 | 
			
		||||
 | 
			
		||||
    def clone_changed(self):
 | 
			
		||||
        self.setText(self.clone.text())
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class MenuBar(QMenuBar): # {{{
 | 
			
		||||
 | 
			
		||||
    def __init__(self, location_manager, parent):
 | 
			
		||||
        QMenuBar.__init__(self, parent)
 | 
			
		||||
        self.gui = parent
 | 
			
		||||
        self.setNativeMenuBar(True)
 | 
			
		||||
 | 
			
		||||
        self.location_manager = location_manager
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
 | 
			
		||||
        self.donate_action = QAction(_('Donate'), self)
 | 
			
		||||
        self.donate_menu = QMenu()
 | 
			
		||||
        self.donate_menu.addAction(self.gui.donate_action)
 | 
			
		||||
        self.donate_action.setMenu(self.donate_menu)
 | 
			
		||||
 | 
			
		||||
    def update_lm_actions(self):
 | 
			
		||||
        for ac in self.added_actions:
 | 
			
		||||
            if ac in self.location_manager.all_actions:
 | 
			
		||||
                ac.setVisible(ac in self.location_manager.available_actions)
 | 
			
		||||
 | 
			
		||||
    def init_bar(self, actions):
 | 
			
		||||
        for ac in self.added_actions:
 | 
			
		||||
            m = ac.menu()
 | 
			
		||||
            if m is not None:
 | 
			
		||||
                m.setVisible(False)
 | 
			
		||||
 | 
			
		||||
        self.clear()
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
 | 
			
		||||
        for what in actions:
 | 
			
		||||
            if what is None:
 | 
			
		||||
                continue
 | 
			
		||||
            elif what == 'Location Manager':
 | 
			
		||||
                for ac in self.location_manager.all_actions:
 | 
			
		||||
                    ac = self.build_menu(ac)
 | 
			
		||||
                    self.addAction(ac)
 | 
			
		||||
                    self.added_actions.append(ac)
 | 
			
		||||
                    ac.setVisible(False)
 | 
			
		||||
            elif what == 'Donate':
 | 
			
		||||
                self.addAction(self.donate_action)
 | 
			
		||||
            elif what in self.gui.iactions:
 | 
			
		||||
                action = self.gui.iactions[what]
 | 
			
		||||
                ac = self.build_menu(action.qaction)
 | 
			
		||||
                self.addAction(ac)
 | 
			
		||||
                self.added_actions.append(ac)
 | 
			
		||||
 | 
			
		||||
    def build_menu(self, action):
 | 
			
		||||
        m = action.menu()
 | 
			
		||||
        ac = MenuAction(action, self)
 | 
			
		||||
        if m is None:
 | 
			
		||||
            m = QMenu()
 | 
			
		||||
            m.addAction(action)
 | 
			
		||||
        ac.setMenu(m)
 | 
			
		||||
        return ac
 | 
			
		||||
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class BarsManager(QObject):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, donate_button, location_manager, parent):
 | 
			
		||||
        QObject.__init__(self, parent)
 | 
			
		||||
        self.donate_button, self.location_manager = (donate_button,
 | 
			
		||||
                location_manager)
 | 
			
		||||
 | 
			
		||||
        bars = [ToolBar(donate_button, location_manager, parent) for i in
 | 
			
		||||
                range(3)]
 | 
			
		||||
        self.main_bars = tuple(bars[:2])
 | 
			
		||||
        self.child_bars = tuple(bars[2:])
 | 
			
		||||
 | 
			
		||||
        self.menu_bar = MenuBar(self.location_manager, self.parent())
 | 
			
		||||
        self.parent().setMenuBar(self.menu_bar)
 | 
			
		||||
 | 
			
		||||
        self.apply_settings()
 | 
			
		||||
        self.init_bars()
 | 
			
		||||
 | 
			
		||||
    def database_changed(self, db):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def bars(self):
 | 
			
		||||
        for x in self.main_bars + self.child_bars:
 | 
			
		||||
            yield x
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def showing_donate(self):
 | 
			
		||||
        for b in self.bars:
 | 
			
		||||
            if b.isVisible() and b.showing_donate:
 | 
			
		||||
                return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def init_bars(self):
 | 
			
		||||
        self.bar_actions = tuple(
 | 
			
		||||
            [gprefs['action-layout-toolbar'+x] for x in ('', '-device')] +
 | 
			
		||||
            [gprefs['action-layout-toolbar-child']] +
 | 
			
		||||
            [gprefs['action-layout-menubar']] +
 | 
			
		||||
            [gprefs['action-layout-menubar-device']]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        for bar, actions in zip(self.bars, self.bar_actions[:3]):
 | 
			
		||||
            bar.init_bar(actions)
 | 
			
		||||
 | 
			
		||||
    def update_bars(self):
 | 
			
		||||
        '''
 | 
			
		||||
        This shows the correct main toolbar and rebuilds the menubar based on
 | 
			
		||||
        whether a device is connected or not. Note that the toolbars are
 | 
			
		||||
        explicitly not rebuilt, this is to workaround a Qt limitation iwth
 | 
			
		||||
        QToolButton's popup menus and modal dialogs. If you want the toolbars
 | 
			
		||||
        rebuilt, call init_bars().
 | 
			
		||||
        '''
 | 
			
		||||
        showing_device = self.location_manager.has_device
 | 
			
		||||
        main_bar = self.main_bars[1 if showing_device else 0]
 | 
			
		||||
        child_bar = self.child_bars[0]
 | 
			
		||||
        for bar in self.bars:
 | 
			
		||||
            bar.setVisible(False)
 | 
			
		||||
            bar.update_lm_actions()
 | 
			
		||||
        if main_bar.added_actions:
 | 
			
		||||
            main_bar.setVisible(True)
 | 
			
		||||
        if child_bar.added_actions:
 | 
			
		||||
            child_bar.setVisible(True)
 | 
			
		||||
 | 
			
		||||
        self.menu_bar.init_bar(self.bar_actions[4 if showing_device else 3])
 | 
			
		||||
        self.menu_bar.update_lm_actions()
 | 
			
		||||
        self.menu_bar.setVisible(bool(self.menu_bar.added_actions))
 | 
			
		||||
 | 
			
		||||
    def apply_settings(self):
 | 
			
		||||
        sz = gprefs['toolbar_icon_size']
 | 
			
		||||
        sz = {'off':0, 'small':24, 'medium':48, 'large':64}[sz]
 | 
			
		||||
        style = Qt.ToolButtonTextUnderIcon
 | 
			
		||||
        if sz > 0 and gprefs['toolbar_text'] == 'never':
 | 
			
		||||
            style = Qt.ToolButtonIconOnly
 | 
			
		||||
 | 
			
		||||
        for bar in self.bars:
 | 
			
		||||
            bar.setIconSize(QSize(sz, sz))
 | 
			
		||||
            bar.setToolButtonStyle(style)
 | 
			
		||||
        self.donate_button.set_normal_icon_size(sz, sz)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -751,6 +751,7 @@ class DeviceMixin(object): # {{{
 | 
			
		||||
            if self.current_view() != self.library_view:
 | 
			
		||||
                self.book_details.reset_info()
 | 
			
		||||
            self.location_manager.update_devices()
 | 
			
		||||
            self.bars_manager.update_bars()
 | 
			
		||||
            self.library_view.set_device_connected(self.device_connected)
 | 
			
		||||
            self.refresh_ondevice()
 | 
			
		||||
        device_signals.device_connection_changed.emit(connected)
 | 
			
		||||
@ -764,6 +765,7 @@ class DeviceMixin(object): # {{{
 | 
			
		||||
        info, cp, fs = job.result
 | 
			
		||||
        self.location_manager.update_devices(cp, fs,
 | 
			
		||||
                self.device_manager.device.icon)
 | 
			
		||||
        self.bars_manager.update_bars()
 | 
			
		||||
        self.status_bar.device_connected(info[0])
 | 
			
		||||
        self.device_manager.books(Dispatcher(self.metadata_downloaded))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -78,22 +78,22 @@
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <layout class="QHBoxLayout" name="horizontalLayout">
 | 
			
		||||
     <item>
 | 
			
		||||
    <layout class="QGridLayout">
 | 
			
		||||
     <item row="0" column="0">
 | 
			
		||||
      <widget class="QPushButton" name="sort_by_author">
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>Sort by author</string>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
     <item row="0" column="1">
 | 
			
		||||
      <widget class="QPushButton" name="sort_by_author_sort">
 | 
			
		||||
       <property name="text">
 | 
			
		||||
        <string>Sort by author sort</string>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
     <item row="1" column="0">
 | 
			
		||||
      <widget class="QPushButton" name="recalc_author_sort">
 | 
			
		||||
       <property name="toolTip">
 | 
			
		||||
        <string>Reset all the author sort values to a value automatically
 | 
			
		||||
@ -105,7 +105,7 @@ generated can be controlled via Preferences->Advanced->Tweaks</string>
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
     <item row="1" column="1">
 | 
			
		||||
      <widget class="QPushButton" name="auth_sort_to_author">
 | 
			
		||||
       <property name="toolTip">
 | 
			
		||||
        <string>Copy author sort to author for every author. You typically use this button
 | 
			
		||||
@ -116,20 +116,7 @@ after changing Preferences->Advanced->Tweaks->Author sort name algorith
 | 
			
		||||
       </property>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <spacer name="horizontalSpacer_3">
 | 
			
		||||
       <property name="orientation">
 | 
			
		||||
        <enum>Qt::Horizontal</enum>
 | 
			
		||||
       </property>
 | 
			
		||||
       <property name="sizeHint" stdset="0">
 | 
			
		||||
        <size>
 | 
			
		||||
         <width>40</width>
 | 
			
		||||
         <height>20</height>
 | 
			
		||||
        </size>
 | 
			
		||||
       </property>
 | 
			
		||||
      </spacer>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
     <item row="1" column="2">
 | 
			
		||||
      <widget class="QDialogButtonBox" name="buttonBox">
 | 
			
		||||
       <property name="sizePolicy">
 | 
			
		||||
        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
 | 
			
		||||
 | 
			
		||||
@ -7,15 +7,15 @@ __docformat__ = 'restructuredtext en'
 | 
			
		||||
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from PyQt4.Qt import (QIcon, Qt, QWidget, QToolBar, QSize,
 | 
			
		||||
    pyqtSignal, QToolButton, QMenu, QMenuBar, QAction,
 | 
			
		||||
from PyQt4.Qt import (QIcon, Qt, QWidget, QSize,
 | 
			
		||||
    pyqtSignal, QToolButton, QMenu,
 | 
			
		||||
    QObject, QVBoxLayout, QSizePolicy, QLabel, QHBoxLayout, QActionGroup)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from calibre.constants import __appname__, isosx
 | 
			
		||||
from calibre.constants import __appname__
 | 
			
		||||
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
 | 
			
		||||
from calibre.gui2.throbber import ThrobbingButton
 | 
			
		||||
from calibre.gui2 import gprefs
 | 
			
		||||
from calibre.gui2.bars import BarsManager
 | 
			
		||||
from calibre.gui2.widgets import ComboBoxWithHelp
 | 
			
		||||
from calibre import human_readable
 | 
			
		||||
 | 
			
		||||
@ -35,6 +35,8 @@ class LocationManager(QObject): # {{{
 | 
			
		||||
        self._mem = []
 | 
			
		||||
        self.tooltips = {}
 | 
			
		||||
 | 
			
		||||
        self.all_actions = []
 | 
			
		||||
 | 
			
		||||
        def ac(name, text, icon, tooltip):
 | 
			
		||||
            icon = QIcon(I(icon))
 | 
			
		||||
            ac = self.location_actions.addAction(icon, text)
 | 
			
		||||
@ -44,7 +46,7 @@ class LocationManager(QObject): # {{{
 | 
			
		||||
            receiver = partial(self._location_selected, name)
 | 
			
		||||
            ac.triggered.connect(receiver)
 | 
			
		||||
            self.tooltips[name] = tooltip
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            m = QMenu(parent)
 | 
			
		||||
            self._mem.append(m)
 | 
			
		||||
            a = m.addAction(icon, tooltip)
 | 
			
		||||
@ -59,6 +61,7 @@ class LocationManager(QObject): # {{{
 | 
			
		||||
            ac.setMenu(m)
 | 
			
		||||
            ac.calibre_name = name
 | 
			
		||||
 | 
			
		||||
            self.all_actions.append(ac)
 | 
			
		||||
            return ac
 | 
			
		||||
 | 
			
		||||
        self.library_action = ac('library', _('Library'), 'lt.png',
 | 
			
		||||
@ -77,7 +80,7 @@ class LocationManager(QObject): # {{{
 | 
			
		||||
            self.switch_menu.addSeparator()
 | 
			
		||||
        else:
 | 
			
		||||
            self.switch_menu = QMenu()
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        self.switch_menu.addAction(choose_action)
 | 
			
		||||
        self.cs_menus = []
 | 
			
		||||
        for t, acs in [(_('Quick switch'), quick_actions),
 | 
			
		||||
@ -91,7 +94,7 @@ class LocationManager(QObject): # {{{
 | 
			
		||||
        self.switch_menu.addSeparator()
 | 
			
		||||
        for ac in switch_actions:
 | 
			
		||||
            self.switch_menu.addAction(ac)
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if self.switch_menu != self.library_action.menu():
 | 
			
		||||
            self.library_action.setMenu(self.switch_menu)
 | 
			
		||||
 | 
			
		||||
@ -234,259 +237,6 @@ class Spacer(QWidget): # {{{
 | 
			
		||||
        self.l.addStretch(10)
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class MenuAction(QAction): # {{{
 | 
			
		||||
 | 
			
		||||
    def __init__(self, clone, parent):
 | 
			
		||||
        QAction.__init__(self, clone.text(), parent)
 | 
			
		||||
        self.clone = clone
 | 
			
		||||
        clone.changed.connect(self.clone_changed)
 | 
			
		||||
 | 
			
		||||
    def clone_changed(self):
 | 
			
		||||
        self.setText(self.clone.text())
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class MenuBar(QMenuBar): # {{{
 | 
			
		||||
 | 
			
		||||
    def __init__(self, location_manager, parent):
 | 
			
		||||
        QMenuBar.__init__(self, parent)
 | 
			
		||||
        self.gui = parent
 | 
			
		||||
        self.setNativeMenuBar(True)
 | 
			
		||||
 | 
			
		||||
        self.location_manager = location_manager
 | 
			
		||||
        self.location_manager.locations_changed.connect(self.build_bar)
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
 | 
			
		||||
        self.donate_action = QAction(_('Donate'), self)
 | 
			
		||||
        self.donate_menu = QMenu()
 | 
			
		||||
        self.donate_menu.addAction(self.gui.donate_action)
 | 
			
		||||
        self.donate_action.setMenu(self.donate_menu)
 | 
			
		||||
        self.build_bar()
 | 
			
		||||
 | 
			
		||||
    def build_bar(self, changed_action=None):
 | 
			
		||||
        showing_device = self.location_manager.has_device
 | 
			
		||||
        actions = '-device' if showing_device else ''
 | 
			
		||||
        actions = gprefs['action-layout-menubar'+actions]
 | 
			
		||||
 | 
			
		||||
        show_main = len(actions) > 0
 | 
			
		||||
        self.setVisible(show_main)
 | 
			
		||||
 | 
			
		||||
        for ac in self.added_actions:
 | 
			
		||||
            m = ac.menu()
 | 
			
		||||
            if m is not None:
 | 
			
		||||
                m.setVisible(False)
 | 
			
		||||
 | 
			
		||||
        self.clear()
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
        self.action_map = {}
 | 
			
		||||
 | 
			
		||||
        for what in actions:
 | 
			
		||||
            if what is None:
 | 
			
		||||
                continue
 | 
			
		||||
            elif what == 'Location Manager':
 | 
			
		||||
                for ac in self.location_manager.available_actions:
 | 
			
		||||
                    ac = self.build_menu(ac)
 | 
			
		||||
                    self.addAction(ac)
 | 
			
		||||
                    self.added_actions.append(ac)
 | 
			
		||||
            elif what == 'Donate':
 | 
			
		||||
                self.addAction(self.donate_action)
 | 
			
		||||
            elif what in self.gui.iactions:
 | 
			
		||||
                action = self.gui.iactions[what]
 | 
			
		||||
                ac = self.build_menu(action.qaction)
 | 
			
		||||
                self.addAction(ac)
 | 
			
		||||
                self.added_actions.append(ac)
 | 
			
		||||
 | 
			
		||||
    def build_menu(self, action):
 | 
			
		||||
        m = action.menu()
 | 
			
		||||
        ac = MenuAction(action, self)
 | 
			
		||||
        if m is None:
 | 
			
		||||
            m = QMenu()
 | 
			
		||||
            m.addAction(action)
 | 
			
		||||
        ac.setMenu(m)
 | 
			
		||||
        return ac
 | 
			
		||||
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class BaseToolBar(QToolBar): # {{{
 | 
			
		||||
 | 
			
		||||
    def __init__(self, parent):
 | 
			
		||||
        QToolBar.__init__(self, parent)
 | 
			
		||||
        self.setContextMenuPolicy(Qt.PreventContextMenu)
 | 
			
		||||
        self.setMovable(False)
 | 
			
		||||
        self.setFloatable(False)
 | 
			
		||||
        self.setOrientation(Qt.Horizontal)
 | 
			
		||||
        self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
 | 
			
		||||
        self.setStyleSheet('QToolButton:checked { font-weight: bold }')
 | 
			
		||||
        self.preferred_width = self.sizeHint().width()
 | 
			
		||||
 | 
			
		||||
    def resizeEvent(self, ev):
 | 
			
		||||
        QToolBar.resizeEvent(self, ev)
 | 
			
		||||
        style = self.get_text_style()
 | 
			
		||||
        self.setToolButtonStyle(style)
 | 
			
		||||
        if hasattr(self, 'd_widget') and hasattr(self.d_widget, 'filler'):
 | 
			
		||||
            self.d_widget.filler.setVisible(style != Qt.ToolButtonIconOnly)
 | 
			
		||||
 | 
			
		||||
    def get_text_style(self):
 | 
			
		||||
        style = Qt.ToolButtonTextUnderIcon
 | 
			
		||||
        s = gprefs['toolbar_icon_size']
 | 
			
		||||
        if s != 'off':
 | 
			
		||||
            p = gprefs['toolbar_text']
 | 
			
		||||
            if p == 'never':
 | 
			
		||||
                style = Qt.ToolButtonIconOnly
 | 
			
		||||
            elif p == 'auto' and self.preferred_width > self.width()+35:
 | 
			
		||||
                style = Qt.ToolButtonIconOnly
 | 
			
		||||
        return style
 | 
			
		||||
 | 
			
		||||
    def contextMenuEvent(self, *args):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class ToolBar(BaseToolBar): # {{{
 | 
			
		||||
 | 
			
		||||
    def __init__(self, donate, location_manager, child_bar, parent):
 | 
			
		||||
        BaseToolBar.__init__(self, parent)
 | 
			
		||||
        self.gui = parent
 | 
			
		||||
        self.child_bar = child_bar
 | 
			
		||||
        self.donate_button = donate
 | 
			
		||||
        self.apply_settings()
 | 
			
		||||
 | 
			
		||||
        self.location_manager = location_manager
 | 
			
		||||
        self.location_manager.locations_changed.connect(self.build_bar)
 | 
			
		||||
        donate.setAutoRaise(True)
 | 
			
		||||
        donate.setCursor(Qt.PointingHandCursor)
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
        self.build_bar()
 | 
			
		||||
        self.setAcceptDrops(True)
 | 
			
		||||
 | 
			
		||||
    def apply_settings(self):
 | 
			
		||||
        sz = gprefs['toolbar_icon_size']
 | 
			
		||||
        sz = {'off':0, 'small':24, 'medium':48, 'large':64}[sz]
 | 
			
		||||
        self.setIconSize(QSize(sz, sz))
 | 
			
		||||
        self.child_bar.setIconSize(QSize(sz, sz))
 | 
			
		||||
        style = Qt.ToolButtonTextUnderIcon
 | 
			
		||||
        if sz > 0 and gprefs['toolbar_text'] == 'never':
 | 
			
		||||
            style = Qt.ToolButtonIconOnly
 | 
			
		||||
        self.setToolButtonStyle(style)
 | 
			
		||||
        self.child_bar.setToolButtonStyle(style)
 | 
			
		||||
        self.donate_button.set_normal_icon_size(sz, sz)
 | 
			
		||||
 | 
			
		||||
    def build_bar(self):
 | 
			
		||||
        self.showing_donate = False
 | 
			
		||||
        showing_device = self.location_manager.has_device
 | 
			
		||||
        mactions = '-device' if showing_device else ''
 | 
			
		||||
        mactions = gprefs['action-layout-toolbar'+mactions]
 | 
			
		||||
        cactions = gprefs['action-layout-toolbar-child']
 | 
			
		||||
 | 
			
		||||
        show_main = len(mactions) > 0
 | 
			
		||||
        self.setVisible(show_main)
 | 
			
		||||
        show_child = len(cactions) > 0
 | 
			
		||||
        self.child_bar.setVisible(show_child)
 | 
			
		||||
 | 
			
		||||
        for ac in self.added_actions:
 | 
			
		||||
            m = ac.menu()
 | 
			
		||||
            if m is not None:
 | 
			
		||||
                m.setVisible(False)
 | 
			
		||||
 | 
			
		||||
        self.clear()
 | 
			
		||||
        self.child_bar.clear()
 | 
			
		||||
        self.added_actions = []
 | 
			
		||||
 | 
			
		||||
        for bar, actions in ((self, mactions), (self.child_bar, cactions)):
 | 
			
		||||
            for what in actions:
 | 
			
		||||
                if what is None:
 | 
			
		||||
                    bar.addSeparator()
 | 
			
		||||
                elif what == 'Location Manager':
 | 
			
		||||
                    for ac in self.location_manager.available_actions:
 | 
			
		||||
                        bar.addAction(ac)
 | 
			
		||||
                        bar.added_actions.append(ac)
 | 
			
		||||
                        bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup)
 | 
			
		||||
                elif what == 'Donate':
 | 
			
		||||
                    self.d_widget = QWidget()
 | 
			
		||||
                    self.d_widget.setLayout(QVBoxLayout())
 | 
			
		||||
                    self.d_widget.layout().addWidget(self.donate_button)
 | 
			
		||||
                    if isosx:
 | 
			
		||||
                        self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
 | 
			
		||||
                        self.d_widget.layout().setContentsMargins(0,0,0,0)
 | 
			
		||||
                        self.d_widget.setContentsMargins(0,0,0,0)
 | 
			
		||||
                        self.d_widget.filler = QLabel(u'\u00a0')
 | 
			
		||||
                        self.d_widget.layout().addWidget(self.d_widget.filler)
 | 
			
		||||
                    bar.addWidget(self.d_widget)
 | 
			
		||||
                    self.showing_donate = True
 | 
			
		||||
                elif what in self.gui.iactions:
 | 
			
		||||
                    action = self.gui.iactions[what]
 | 
			
		||||
                    bar.addAction(action.qaction)
 | 
			
		||||
                    self.added_actions.append(action.qaction)
 | 
			
		||||
                    self.setup_tool_button(bar, action.qaction, action.popup_type)
 | 
			
		||||
        self.preferred_width = self.sizeHint().width()
 | 
			
		||||
        self.child_bar.preferred_width = self.child_bar.sizeHint().width()
 | 
			
		||||
 | 
			
		||||
    def setup_tool_button(self, bar, ac, menu_mode=None):
 | 
			
		||||
        ch = bar.widgetForAction(ac)
 | 
			
		||||
        if ch is None:
 | 
			
		||||
            ch = self.child_bar.widgetForAction(ac)
 | 
			
		||||
        ch.setCursor(Qt.PointingHandCursor)
 | 
			
		||||
        ch.setAutoRaise(True)
 | 
			
		||||
        if ac.menu() is not None and menu_mode is not None:
 | 
			
		||||
            ch.setPopupMode(menu_mode)
 | 
			
		||||
        return ch
 | 
			
		||||
 | 
			
		||||
    def database_changed(self, db):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    #support drag&drop from/to library from/to reader/card
 | 
			
		||||
    def dragEnterEvent(self, event):
 | 
			
		||||
        md = event.mimeData()
 | 
			
		||||
        if md.hasFormat("application/calibre+from_library") or \
 | 
			
		||||
           md.hasFormat("application/calibre+from_device"):
 | 
			
		||||
            event.setDropAction(Qt.CopyAction)
 | 
			
		||||
            event.accept()
 | 
			
		||||
        else:
 | 
			
		||||
            event.ignore()
 | 
			
		||||
 | 
			
		||||
    def dragMoveEvent(self, event):
 | 
			
		||||
        allowed = False
 | 
			
		||||
        md = event.mimeData()
 | 
			
		||||
        #Drop is only allowed in the location manager widget's different from the selected one
 | 
			
		||||
        for ac in self.location_manager.available_actions:
 | 
			
		||||
            w = self.widgetForAction(ac)
 | 
			
		||||
            if w is not None:
 | 
			
		||||
                if ( md.hasFormat("application/calibre+from_library") or \
 | 
			
		||||
                     md.hasFormat("application/calibre+from_device") ) and \
 | 
			
		||||
                        w.geometry().contains(event.pos()) and \
 | 
			
		||||
                        isinstance(w, QToolButton) and not w.isChecked():
 | 
			
		||||
                    allowed = True
 | 
			
		||||
                    break
 | 
			
		||||
        if allowed:
 | 
			
		||||
            event.acceptProposedAction()
 | 
			
		||||
        else:
 | 
			
		||||
            event.ignore()
 | 
			
		||||
 | 
			
		||||
    def dropEvent(self, event):
 | 
			
		||||
        data = event.mimeData()
 | 
			
		||||
 | 
			
		||||
        mime = 'application/calibre+from_library'
 | 
			
		||||
        if data.hasFormat(mime):
 | 
			
		||||
            ids = list(map(int, str(data.data(mime)).split()))
 | 
			
		||||
            tgt = None
 | 
			
		||||
            for ac in self.location_manager.available_actions:
 | 
			
		||||
                w = self.widgetForAction(ac)
 | 
			
		||||
                if w is not None and w.geometry().contains(event.pos()):
 | 
			
		||||
                    tgt = ac.calibre_name
 | 
			
		||||
            if tgt is not None:
 | 
			
		||||
                if tgt == 'main':
 | 
			
		||||
                    tgt = None
 | 
			
		||||
                self.gui.sync_to_device(tgt, False, send_ids=ids)
 | 
			
		||||
                event.accept()
 | 
			
		||||
 | 
			
		||||
        mime = 'application/calibre+from_device'
 | 
			
		||||
        if data.hasFormat(mime):
 | 
			
		||||
            paths = [unicode(u.toLocalFile()) for u in data.urls()]
 | 
			
		||||
            if paths:
 | 
			
		||||
                self.gui.iactions['Add Books'].add_books_from_device(
 | 
			
		||||
                        self.gui.current_view(), paths=paths)
 | 
			
		||||
                event.accept()
 | 
			
		||||
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
class MainWindowMixin(object): # {{{
 | 
			
		||||
 | 
			
		||||
@ -507,13 +257,13 @@ class MainWindowMixin(object): # {{{
 | 
			
		||||
        self.iactions['Fetch News'].init_scheduler(db)
 | 
			
		||||
 | 
			
		||||
        self.search_bar = SearchBar(self)
 | 
			
		||||
        self.child_bar = BaseToolBar(self)
 | 
			
		||||
        self.tool_bar = ToolBar(self.donate_button,
 | 
			
		||||
                self.location_manager, self.child_bar, self)
 | 
			
		||||
        self.addToolBar(Qt.TopToolBarArea, self.tool_bar)
 | 
			
		||||
        self.addToolBar(Qt.BottomToolBarArea, self.child_bar)
 | 
			
		||||
        self.menu_bar = MenuBar(self.location_manager, self)
 | 
			
		||||
        self.setMenuBar(self.menu_bar)
 | 
			
		||||
        self.bars_manager = BarsManager(self.donate_button,
 | 
			
		||||
                self.location_manager, self)
 | 
			
		||||
        for bar in self.bars_manager.main_bars:
 | 
			
		||||
            self.addToolBar(Qt.TopToolBarArea, bar)
 | 
			
		||||
        for bar in self.bars_manager.child_bars:
 | 
			
		||||
            self.addToolBar(Qt.BottomToolBarArea, bar)
 | 
			
		||||
        self.bars_manager.update_bars()
 | 
			
		||||
        self.setUnifiedTitleAndToolBarOnMac(True)
 | 
			
		||||
 | 
			
		||||
        l = self.centralwidget.layout()
 | 
			
		||||
 | 
			
		||||
@ -361,10 +361,9 @@ class Preferences(QMainWindow):
 | 
			
		||||
            self.gui.tags_view.recount()
 | 
			
		||||
            self.gui.create_device_menu()
 | 
			
		||||
            self.gui.set_device_menu_items_state(bool(self.gui.device_connected))
 | 
			
		||||
            self.gui.tool_bar.build_bar()
 | 
			
		||||
            self.gui.menu_bar.build_bar()
 | 
			
		||||
            self.gui.bars_manager.apply_settings()
 | 
			
		||||
            self.gui.bars_manager.update_bars()
 | 
			
		||||
            self.gui.build_context_menus()
 | 
			
		||||
            self.gui.tool_bar.apply_settings()
 | 
			
		||||
 | 
			
		||||
        return QMainWindow.closeEvent(self, *args)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,17 +5,20 @@ __license__   = 'GPL v3'
 | 
			
		||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
 | 
			
		||||
__docformat__ = 'restructuredtext en'
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from PyQt4.Qt import Qt, QLineEdit, QComboBox, SIGNAL, QListWidgetItem
 | 
			
		||||
 | 
			
		||||
from calibre.customize.ui import is_disabled
 | 
			
		||||
from calibre.gui2 import error_dialog
 | 
			
		||||
from calibre.gui2 import error_dialog, question_dialog
 | 
			
		||||
from calibre.gui2.device import device_name_for_plugboards
 | 
			
		||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
 | 
			
		||||
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
 | 
			
		||||
from calibre.gui2.preferences.plugboard_ui import Ui_Form
 | 
			
		||||
from calibre.customize.ui import metadata_writers, device_plugins
 | 
			
		||||
from calibre.library.save_to_disk import plugboard_any_format_value, \
 | 
			
		||||
                        plugboard_any_device_value, plugboard_save_to_disk_value
 | 
			
		||||
                    plugboard_any_device_value, plugboard_save_to_disk_value, \
 | 
			
		||||
                    find_plugboard
 | 
			
		||||
from calibre.library.server.content import plugboard_content_server_value, \
 | 
			
		||||
                                        plugboard_content_server_formats
 | 
			
		||||
from calibre.utils.formatter import validation_formatter
 | 
			
		||||
@ -46,9 +49,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
			
		||||
    def genesis(self, gui):
 | 
			
		||||
        self.gui = gui
 | 
			
		||||
        self.db = gui.library_view.model().db
 | 
			
		||||
        self.current_plugboards = self.db.prefs.get('plugboards',{})
 | 
			
		||||
        self.current_device = None
 | 
			
		||||
        self.current_format = None
 | 
			
		||||
 | 
			
		||||
    def initialize(self):
 | 
			
		||||
        def field_cmp(x, y):
 | 
			
		||||
@ -64,6 +64,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
			
		||||
 | 
			
		||||
        ConfigWidgetBase.initialize(self)
 | 
			
		||||
 | 
			
		||||
        self.current_plugboards = copy.deepcopy(self.db.prefs.get('plugboards',{}))
 | 
			
		||||
        self.current_device = None
 | 
			
		||||
        self.current_format = None
 | 
			
		||||
 | 
			
		||||
        if self.gui.device_manager.connected_device is not None:
 | 
			
		||||
            self.device_label.setText(_('Device currently connected: ') +
 | 
			
		||||
                    self.gui.device_manager.connected_device.__class__.__name__)
 | 
			
		||||
@ -196,51 +200,66 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
			
		||||
            return
 | 
			
		||||
        self.clear_fields(edit_boxes=True)
 | 
			
		||||
        self.current_device = unicode(txt)
 | 
			
		||||
        error = False
 | 
			
		||||
        if self.current_format == plugboard_any_format_value:
 | 
			
		||||
            # user specified any format.
 | 
			
		||||
            for f in self.current_plugboards:
 | 
			
		||||
                devs = set(self.current_plugboards[f])
 | 
			
		||||
                if self.current_device != plugboard_save_to_disk_value and \
 | 
			
		||||
                        plugboard_any_device_value in devs:
 | 
			
		||||
                    # specific format/any device in list. conflict.
 | 
			
		||||
                    # note: any device does not match save_to_disk
 | 
			
		||||
                    error = True
 | 
			
		||||
                    break
 | 
			
		||||
                if self.current_device in devs:
 | 
			
		||||
                    # specific format/current device in list. conflict
 | 
			
		||||
                    error = True
 | 
			
		||||
                    break
 | 
			
		||||
                if self.current_device == plugboard_any_device_value:
 | 
			
		||||
                    # any device and a specific device already there. conflict
 | 
			
		||||
                    error = True
 | 
			
		||||
                    break
 | 
			
		||||
        else:
 | 
			
		||||
            # user specified specific format.
 | 
			
		||||
            for f in self.current_plugboards:
 | 
			
		||||
                devs = set(self.current_plugboards[f])
 | 
			
		||||
                if f == plugboard_any_format_value and \
 | 
			
		||||
                                self.current_device in devs:
 | 
			
		||||
                    # any format/same device in list. conflict.
 | 
			
		||||
                    error = True
 | 
			
		||||
                    break
 | 
			
		||||
                if f == self.current_format and self.current_device in devs:
 | 
			
		||||
                    # current format/current device in list. conflict
 | 
			
		||||
                    error = True
 | 
			
		||||
                    break
 | 
			
		||||
                if f == self.current_format and plugboard_any_device_value in devs:
 | 
			
		||||
                    # current format/any device in list. conflict
 | 
			
		||||
                    error = True
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
        if error:
 | 
			
		||||
        if self.current_format in self.current_plugboards and \
 | 
			
		||||
                self.current_device in self.current_plugboards[self.current_format]:
 | 
			
		||||
            error_dialog(self, '',
 | 
			
		||||
                     _('That format and device already has a plugboard or '
 | 
			
		||||
                       'conflicts with another plugboard.'),
 | 
			
		||||
                     _('That format and device already has a plugboard.'),
 | 
			
		||||
                     show=True)
 | 
			
		||||
            self.new_device.setCurrentIndex(0)
 | 
			
		||||
            return
 | 
			
		||||
        if self.current_device in self.device_to_formats_map:
 | 
			
		||||
 | 
			
		||||
        # If we have a specific format/device combination, check if a more
 | 
			
		||||
        # general combination matches.
 | 
			
		||||
        if self.current_format != plugboard_any_format_value and \
 | 
			
		||||
                self.current_device != plugboard_any_device_value:
 | 
			
		||||
            if find_plugboard(self.current_device, self.current_format,
 | 
			
		||||
                      self.current_plugboards):
 | 
			
		||||
                if not question_dialog(self.gui,
 | 
			
		||||
                        _('Possibly override plugboard?'),
 | 
			
		||||
                        _('A more general plugboard already exists for '
 | 
			
		||||
                          'that format and device. '
 | 
			
		||||
                          'Are you sure you want to add the new plugboard?')):
 | 
			
		||||
                    self.new_device.setCurrentIndex(0)
 | 
			
		||||
                    return
 | 
			
		||||
 | 
			
		||||
        # If we have a specific format, check if we are adding a possibly-
 | 
			
		||||
        # covered plugboard
 | 
			
		||||
        if self.current_format != plugboard_any_format_value:
 | 
			
		||||
            if self.current_format in self.current_plugboards:
 | 
			
		||||
                if self.current_device == plugboard_any_device_value:
 | 
			
		||||
                    if not question_dialog(self.gui,
 | 
			
		||||
                               _('Add possibly overridden plugboard?'),
 | 
			
		||||
                               _('More specific device plugboards exist for '
 | 
			
		||||
                                 'that format. '
 | 
			
		||||
                                 'Are you sure you want to add the new plugboard?')):
 | 
			
		||||
                        self.new_device.setCurrentIndex(0)
 | 
			
		||||
                        return
 | 
			
		||||
        # We are adding an 'any format' entry. Check if we are adding a specific
 | 
			
		||||
        # device and if so, does some other plugboard match that device.
 | 
			
		||||
        elif self.current_device != plugboard_any_device_value:
 | 
			
		||||
            for fmt in self.current_plugboards:
 | 
			
		||||
                if find_plugboard(self.current_device, fmt, self.current_plugboards):
 | 
			
		||||
                    if not question_dialog(self.gui,
 | 
			
		||||
                            _('Really add plugboard?'),
 | 
			
		||||
                            _('A different plugboard matches that format and '
 | 
			
		||||
                              'device combination. '
 | 
			
		||||
                              'Are you sure you want to add the new plugboard?')):
 | 
			
		||||
                        self.new_device.setCurrentIndex(0)
 | 
			
		||||
                        return
 | 
			
		||||
        # We are adding an any format/any device entry, which will be overridden
 | 
			
		||||
        # by any other entry. Ask if such entries exist.
 | 
			
		||||
        elif len(self.current_plugboards):
 | 
			
		||||
            if not question_dialog(self.gui,
 | 
			
		||||
                       _('Add possibly overridden plugboard?'),
 | 
			
		||||
                       _('More specific format and device plugboards '
 | 
			
		||||
                         'already exist. '
 | 
			
		||||
                         'Are you sure you want to add the new plugboard?')):
 | 
			
		||||
                self.new_device.setCurrentIndex(0)
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
        if self.current_format != plugboard_any_format_value and \
 | 
			
		||||
                    self.current_device in self.device_to_formats_map:
 | 
			
		||||
            allowable_formats = self.device_to_formats_map[self.current_device]
 | 
			
		||||
            if self.current_format not in allowable_formats:
 | 
			
		||||
                error_dialog(self, '',
 | 
			
		||||
 | 
			
		||||
@ -317,6 +317,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
			
		||||
            am.restore_defaults()
 | 
			
		||||
        self.changed_signal.emit()
 | 
			
		||||
 | 
			
		||||
    def refresh_gui(self, gui):
 | 
			
		||||
        gui.bars_manager.init_bars()
 | 
			
		||||
        gui.bars_manager.update_bars()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    from PyQt4.Qt import QApplication
 | 
			
		||||
 | 
			
		||||
@ -288,8 +288,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
 | 
			
		||||
                self.db_images.reset()
 | 
			
		||||
 | 
			
		||||
        self.library_view.model().count_changed()
 | 
			
		||||
        self.tool_bar.database_changed(self.library_view.model().db)
 | 
			
		||||
        self.library_view.model().database_changed.connect(self.tool_bar.database_changed,
 | 
			
		||||
        self.bars_manager.database_changed(self.library_view.model().db)
 | 
			
		||||
        self.library_view.model().database_changed.connect(self.bars_manager.database_changed,
 | 
			
		||||
                type=Qt.QueuedConnection)
 | 
			
		||||
 | 
			
		||||
        ########################### Tags Browser ##############################
 | 
			
		||||
@ -324,7 +324,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
 | 
			
		||||
 | 
			
		||||
        self.read_settings()
 | 
			
		||||
        self.finalize_layout()
 | 
			
		||||
        if self.tool_bar.showing_donate:
 | 
			
		||||
        if self.bars_manager.showing_donate:
 | 
			
		||||
            self.donate_button.start_animation()
 | 
			
		||||
        self.set_window_title()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1145,7 +1145,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
 | 
			
		||||
            self.notify('metadata', [id])
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def delete_book(self, id, notify=True, commit=True):
 | 
			
		||||
    def delete_book(self, id, notify=True, commit=True, permanent=False):
 | 
			
		||||
        '''
 | 
			
		||||
        Removes book from the result cache and the underlying database.
 | 
			
		||||
        If you set commit to False, you must call clean() manually afterwards
 | 
			
		||||
@ -1155,10 +1155,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
 | 
			
		||||
        except:
 | 
			
		||||
            path = None
 | 
			
		||||
        if path and os.path.exists(path):
 | 
			
		||||
            self.rmtree(path)
 | 
			
		||||
            self.rmtree(path, permanent=permanent)
 | 
			
		||||
            parent = os.path.dirname(path)
 | 
			
		||||
            if len(os.listdir(parent)) == 0:
 | 
			
		||||
                self.rmtree(parent)
 | 
			
		||||
                self.rmtree(parent, permanent=permanent)
 | 
			
		||||
        self.conn.execute('DELETE FROM books WHERE id=?', (id,))
 | 
			
		||||
        if commit:
 | 
			
		||||
            self.conn.commit()
 | 
			
		||||
 | 
			
		||||
@ -56,16 +56,17 @@ for x in FORMAT_ARG_DESCS:
 | 
			
		||||
def find_plugboard(device_name, format, plugboards):
 | 
			
		||||
    cpb = None
 | 
			
		||||
    if format in plugboards:
 | 
			
		||||
        cpb = plugboards[format]
 | 
			
		||||
    elif plugboard_any_format_value in plugboards:
 | 
			
		||||
        cpb = plugboards[plugboard_any_format_value]
 | 
			
		||||
    if cpb is not None:
 | 
			
		||||
        if device_name in cpb:
 | 
			
		||||
            cpb = cpb[device_name]
 | 
			
		||||
        elif plugboard_any_device_value in cpb:
 | 
			
		||||
            cpb = cpb[plugboard_any_device_value]
 | 
			
		||||
        else:
 | 
			
		||||
            cpb = None
 | 
			
		||||
        pb = plugboards[format]
 | 
			
		||||
        if device_name in pb:
 | 
			
		||||
            cpb = pb[device_name]
 | 
			
		||||
        elif plugboard_any_device_value in pb:
 | 
			
		||||
            cpb = pb[plugboard_any_device_value]
 | 
			
		||||
    if not cpb and plugboard_any_format_value in plugboards:
 | 
			
		||||
        pb = plugboards[plugboard_any_format_value]
 | 
			
		||||
        if device_name in pb:
 | 
			
		||||
            cpb = pb[device_name]
 | 
			
		||||
        elif plugboard_any_device_value in pb:
 | 
			
		||||
            cpb = pb[plugboard_any_device_value]
 | 
			
		||||
    if DEBUG:
 | 
			
		||||
        prints('Device using plugboard', format, device_name, cpb)
 | 
			
		||||
    return cpb
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -24,6 +24,7 @@ elif isosx:
 | 
			
		||||
                path = path.decode(filesystem_encoding)
 | 
			
		||||
            u.send2trash(path)
 | 
			
		||||
 | 
			
		||||
can_recycle = callable(recycle)
 | 
			
		||||
 | 
			
		||||
def delete_file(path):
 | 
			
		||||
    if callable(recycle):
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user