diff --git a/resources/images/devices/folder.svg b/resources/images/devices/folder.svg index 74c1d628e4..e0d6f6b8be 100644 --- a/resources/images/devices/folder.svg +++ b/resources/images/devices/folder.svg @@ -9,544 +9,700 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - x="0.0000000" - y="0.0000000" - width="48.000000px" - height="48.000000px" - id="svg1" + width="128" + height="128" + id="svg2811" sodipodi:version="0.32" - inkscape:version="0.44" - sodipodi:docname="folder.svg" - sodipodi:docbase="/home/lapo/Icone/Crux/crux-icon-theme/scalable/places" - inkscape:export-filename="/home/lapo/Icone/Crux/folderx-daritaliare.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> + inkscape:version="0.45.1" + version="1.0" + sodipodi:docname="folder-downloads.svgz" + inkscape:output_extension="org.inkscape.output.svgz.inkscape" + inkscape:export-filename="folder-downloads.png" + inkscape:export-xdpi="11.25" + inkscape:export-ydpi="11.25" + sodipodi:docbase="/home/david/oxygen/trunk/scalable/places"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id="metadata2816"> image/svg+xml - Folder - - - Lapo Calamandrei - - - 2006-06-26 - - - - - folder - directory - storage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + id="layer1"> + - + id="g17" + style="opacity:0.6;filter:url(#filter2807)" + transform="matrix(1.0033404,0,0,1,-8.2374684,8)"> - + d="M 132,96 C 132,98.2 128.4,100 124,100 L 20,100 C 15.6,100 12,98.2 12,96 C 12,93.8 15.6,92 20,92 L 124,92 C 128.4,92 132,93.8 132,96 z" + id="path19" /> + + + + + + id="g2450" + inkscape:label="Livello 1" + transform="translate(-2.4797995e-7,16)"> - - - - - - - - - + d="M 88,103.99999 C 88,106.20914 77.254827,108 63.999997,108 C 50.745166,108 40,106.20914 40,103.99999 C 40,101.79086 50.745166,100 63.999997,100 C 77.254827,100 88,101.79086 88,103.99999 L 88,103.99999 z " + style="opacity:0.3;fill:url(#radialGradient4545);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" + id="path4543" /> + transform="matrix(1,0,0,0.9756098,-72.426501,80.585366)" + id="g9589"> + style="fill:url(#linearGradient9614)" + d="M 122.40803,-58 C 121.11995,-58 120.07225,-56.801009 120.07225,-55.327765 C 120.07225,-55.327765 120.07225,-20.243475 120.07225,-17.752469 C 118.07565,-17.752469 105.76289,-17.752469 105.76289,-17.752469 C 104.82364,-17.752469 103.98018,-17.113701 103.61327,-16.125567 C 103.61266,-16.122801 103.4265,-15.080231 103.4265,-15.080231 C 103.4265,-14.384954 103.65845,-13.726198 104.08019,-13.22662 L 134.74308,23.18138 C 135.18048,23.70094 135.7944,24 136.42579,24 C 137.05778,24 137.6705,23.70094 138.1085,23.18138 L 168.772,-13.22662 C 169.42327,-14.000449 169.60703,-15.136741 169.23953,-16.125567 C 168.87081,-17.115074 168.02735,-17.751777 167.08929,-17.751777 C 167.08929,-17.751777 154.77654,-17.751777 152.77994,-17.751777 C 152.77994,-20.243475 152.77994,-55.327076 152.77994,-55.327076 C 152.77933,-56.801009 151.73164,-58 150.44355,-58 L 122.40803,-58 z " + id="path49" /> + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + style="opacity:0.5;fill:#ffffff" + enable-background="new " + d="M 152.77933,-15.331057 C 154.77534,-15.331057 167.08869,-15.331057 167.08869,-15.331057 C 168.02794,-15.331057 168.87021,-14.692971 169.23832,-13.704155 C 169.2594,-13.646273 169.27387,-13.585625 169.29193,-13.527062 C 169.48051,-14.144468 169.47147,-14.830788 169.23832,-15.457162 C 168.87021,-16.445977 168.02614,-17.084064 167.08869,-17.084064 C 167.08869,-17.084064 154.77534,-17.084064 152.77933,-17.084064 C 152.77933,-16.201364 152.77933,-15.589458 152.77933,-15.331057 z " + id="path74" /> + + + + + + + + diff --git a/resources/images/user_profile.svg b/resources/images/user_profile.svg index 2fc0eea150..0aecc0c1f7 100644 --- a/resources/images/user_profile.svg +++ b/resources/images/user_profile.svg @@ -1,6 +1,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + inkscape:version="0.46" + version="1.0" + sodipodi:docname="im-user.svgz" + inkscape:output_extension="org.inkscape.output.svgz.inkscape" + sodipodi:docbase="/home/pinheiro/pics/oxygen/scalable/mimetypes" + inkscape:export-filename="/home/pinheiro/pics/oxygen/scalable/actions/im-user.png" + inkscape:export-xdpi="180" + inkscape:export-ydpi="180"> + inkscape:current-layer="layer1" + width="128px" + height="128px" + showgrid="false" + inkscape:grid-points="true" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1016" + inkscape:window-height="692" + inkscape:window-x="20" + inkscape:window-y="356"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id="metadata2611"> image/svg+xml - - - - Jakub Steiner - - - http://jimmac.musichall.cz - - - user - person - - - - - - - - - - - - - - - - - - - - - + inkscape:label="Livello 1"> + style="fill:#493a3a;fill-opacity:1;stroke:none;stroke-width:3.1559999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter3766)" + id="path3716" + sodipodi:cx="63.865829" + sodipodi:cy="108.4109" + sodipodi:rx="41.861637" + sodipodi:ry="13.953878" + d="M 105.72747,108.4109 A 41.861637,13.953878 0 1 1 22.004192,108.4109 A 41.861637,13.953878 0 1 1 105.72747,108.4109 z" + transform="matrix(0.8610583,0,0,1.1808092,8.9931858,-19.441249)" /> + + + + + + + + + style="fill:url(#radialGradient4322);fill-opacity:1;stroke:none;stroke-width:3.40000010000000019;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path2563" + sodipodi:cx="58.041332" + sodipodi:cy="37.27911" + sodipodi:rx="29.958668" + sodipodi:ry="29.958668" + d="M 88,37.27911 A 29.958668,29.958668 0 1 1 28.082664,37.27911 A 29.958668,29.958668 0 1 1 88,37.27911 z" + transform="matrix(0.844028,0,0,0.844028,14.927656,-4.6758122)" /> + + + + + + diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index ca93990420..ec895cb8a4 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -436,7 +436,7 @@ from calibre.devices.blackberry.driver import BLACKBERRY from calibre.devices.cybook.driver import CYBOOK from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \ POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK, \ - BOOQ, ELONEX + BOOQ, ELONEX, POCKETBOOK301 from calibre.devices.iliad.driver import ILIAD from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800 from calibre.devices.jetbook.driver import JETBOOK @@ -507,6 +507,7 @@ plugins += [ JETBOOK, SHINEBOOK, POCKETBOOK360, + POCKETBOOK301, KINDLE, KINDLE2, KINDLE_DX, diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index 307531c357..9b7a21a3bb 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -201,4 +201,21 @@ class ELONEX(EB600): def can_handle(cls, dev, debug=False): return dev[3] == 'Elonex' and dev[4] == 'eBook' +class POCKETBOOK301(USBMS): + + name = 'PocketBook 301 Device Interface' + description = _('Communicate with the PocketBook 301 reader.') + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm', 'txt'] + + SUPPORTS_SUB_DIRS = True + + MAIN_MEMORY_VOLUME_LABEL = 'PocketBook 301 Main Memory' + STORAGE_CARD_VOLUME_LABEL = 'PocketBook 301 Storage Card' + + VENDOR_ID = [0x1] + PRODUCT_ID = [0x301] + BCD = [0x132] + diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index ee779aaefa..8708b98d97 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -385,14 +385,6 @@ class EPUBOutput(OutputFormatPlugin): if val and not pval: rule.style.setProperty('padding-left', val) - if stylesheet is not None: - stylesheet.data.add('a { color: inherit; text-decoration: inherit; ' - 'cursor: default; }') - stylesheet.data.add('a[href] { color: blue; ' - 'text-decoration: underline; cursor:pointer; }') - else: - self.oeb.log.warn('No stylesheet found') - # }}} def workaround_sony_quirks(self): # {{{ diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index a397ab903d..8759dd360b 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -62,11 +62,13 @@ def render_rows(data): class CoverView(QWidget): # {{{ - def __init__(self, parent=None): + def __init__(self, vertical, parent=None): QWidget.__init__(self, parent) self.setMaximumSize(QSize(120, 120)) - self.setMinimumSize(QSize(120, 1)) + self.setMinimumSize(QSize(120 if vertical else 20, 120 if vertical else + 20)) self._current_pixmap_size = self.maximumSize() + self.vertical = vertical self.animation = QPropertyAnimation(self, 'current_pixmap_size', self) self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo)) @@ -74,7 +76,8 @@ class CoverView(QWidget): # {{{ self.animation.setStartValue(QSize(0, 0)) self.animation.valueChanged.connect(self.value_changed) - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.setSizePolicy(QSizePolicy.Expanding if vertical else + QSizePolicy.Minimum, QSizePolicy.Expanding) self.default_pixmap = QPixmap(I('book.svg')) self.pixmap = self.default_pixmap @@ -98,8 +101,12 @@ class CoverView(QWidget): # {{{ self.animation.setEndValue(self.current_pixmap_size) def relayout(self, parent_size): - self.setMaximumSize(parent_size.width(), - min(int(parent_size.height()/2.),int(4/3. * parent_size.width())+1)) + if self.vertical: + self.setMaximumSize(parent_size.width(), + min(int(parent_size.height()/2.),int(4/3. * parent_size.width())+1)) + else: + self.setMaximumSize(1+int(3/4. * parent_size.height()), + parent_size.height()) self.resize(self.maximumSize()) self.animation.stop() self.do_layout() @@ -109,8 +116,7 @@ class CoverView(QWidget): # {{{ def show_data(self, data): self.animation.stop() - if data.get('id', True) == self.data.get('id', False): - return + same_item = data.get('id', True) == self.data.get('id', False) self.data = {'id':data.get('id', None)} if data.has_key('cover'): self.pixmap = QPixmap.fromImage(data.pop('cover')) @@ -120,7 +126,8 @@ class CoverView(QWidget): # {{{ self.pixmap = self.default_pixmap self.do_layout() self.update() - self.animation.start() + if not same_item: + self.animation.start() def paintEvent(self, event): canvas_size = self.rect() @@ -147,6 +154,7 @@ class CoverView(QWidget): # {{{ # }}} +# Book Info {{{ class Label(QLabel): mr = pyqtSignal(object) @@ -174,8 +182,9 @@ class Label(QLabel): class BookInfo(QScrollArea): - def __init__(self, parent=None): + def __init__(self, vertical, parent=None): QScrollArea.__init__(self, parent) + self.vertical = vertical self.setWidgetResizable(True) self.label = Label() self.setWidget(self.label) @@ -188,13 +197,25 @@ class BookInfo(QScrollArea): rows = render_rows(data) rows = u'\n'.join([u'%s:%s'%(k,t) for k, t in rows]) - if _('Comments') in data and data[_('Comments')]: - comments = comments_to_html(data[_('Comments')]) - rows += u'%s'%comments + if self.vertical: + if _('Comments') in data and data[_('Comments')]: + comments = comments_to_html(data[_('Comments')]) + rows += u'%s'%comments + self.label.setText(u'%s
'%rows) + else: + comments = '' + if _('Comments') in data: + comments = comments_to_html(data[_('Comments')]) + left_pane = u'%s
'%rows + right_pane = u'
%s
'%comments + self.label.setText(u'
%s%s
' + % (left_pane, right_pane)) - self.label.setText(u'%s
'%rows) -class BookDetails(QWidget): +# }}} + +class BookDetails(QWidget): # {{{ resized = pyqtSignal(object) show_book_info = pyqtSignal() @@ -234,20 +255,26 @@ class BookDetails(QWidget): # }}} - def __init__(self, parent=None): + def __init__(self, vertical, parent=None): QWidget.__init__(self, parent) + self.setAcceptDrops(True) self._layout = QVBoxLayout() - + if not vertical: + self._layout.setDirection(self._layout.LeftToRight) self.setLayout(self._layout) - self.cover_view = CoverView(self) + + self.cover_view = CoverView(vertical, self) self.cover_view.relayout(self.size()) self.resized.connect(self.cover_view.relayout, type=Qt.QueuedConnection) - self._layout.addWidget(self.cover_view, alignment=Qt.AlignHCenter) - self.book_info = BookInfo(self) + self._layout.addWidget(self.cover_view) + self.book_info = BookInfo(vertical, self) self._layout.addWidget(self.book_info) self.book_info.link_clicked.connect(self._link_clicked) self.book_info.mr.connect(self.mouseReleaseEvent) - self.setMinimumSize(QSize(190, 200)) + if vertical: + self.setMinimumSize(QSize(190, 200)) + else: + self.setMinimumSize(120, 120) self.setCursor(Qt.PointingHandCursor) def _link_clicked(self, link): @@ -277,5 +304,5 @@ class BookDetails(QWidget): def reset_info(self): self.show_data({}) - +# }}} diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index ba92c0d301..efda00fc97 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -7,7 +7,7 @@ 0 0 - 884 + 1000 730 @@ -89,7 +89,7 @@ 0 0 - 604 + 720 679 @@ -370,7 +370,7 @@ - + Show &average ratings in the tags browser diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 1277cb06c7..0a82d3b75b 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -8,17 +8,18 @@ __docformat__ = 'restructuredtext en' import functools from PyQt4.Qt import QMenu, Qt, pyqtSignal, QToolButton, QIcon, QStackedWidget, \ - QWidget, QHBoxLayout, QToolBar, QSize, QSizePolicy + QSize, QSizePolicy, QStatusBar from calibre.utils.config import prefs from calibre.ebooks import BOOK_EXTENSIONS -from calibre.constants import isosx, __appname__ +from calibre.constants import isosx, __appname__, preferred_encoding from calibre.gui2 import config, is_widescreen from calibre.gui2.library.views import BooksView, DeviceBooksView from calibre.gui2.widgets import Splitter from calibre.gui2.tag_view import TagBrowserWidget -from calibre.gui2.status import StatusBar, HStatusBar from calibre.gui2.book_details import BookDetails +from calibre.gui2.notify import get_notifier + _keep_refs = [] @@ -332,26 +333,24 @@ class Stack(QStackedWidget): # {{{ # }}} -class SideBar(QToolBar): # {{{ +class StatusBar(QStatusBar): # {{{ + def initialize(self, systray=None): + self.systray = systray + self.notifier = get_notifier(systray) - def __init__(self, splitters, jobs_button, parent=None): - QToolBar.__init__(self, _('Side bar'), parent) - self.setOrientation(Qt.Vertical) - self.setMovable(False) - self.setFloatable(False) - self.setToolButtonStyle(Qt.ToolButtonIconOnly) - self.setIconSize(QSize(48, 48)) - self.spacer = QWidget(self) - self.spacer.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) - for s in splitters: - self.addWidget(s.button) - self.addWidget(self.spacer) - self.addWidget(jobs_button) + def show_message(self, msg, timeout=0): + QStatusBar.showMessage(self, msg, timeout) + if self.notifier is not None and not config['disable_tray_notification']: + if isosx and isinstance(msg, unicode): + try: + msg = msg.encode(preferred_encoding) + except UnicodeEncodeError: + msg = msg.encode('utf-8') + self.notifier(msg) - for ch in self.children(): - if isinstance(ch, QToolButton): - ch.setCursor(Qt.PointingHandCursor) + def clear_message(self): + QStatusBar.clearMessage(self) # }}} @@ -361,45 +360,46 @@ class LayoutMixin(object): # {{{ self.setupUi(self) self.setWindowTitle(__appname__) - if config['gui_layout'] == 'narrow': - self.status_bar = self.book_details = StatusBar(self) + if config['gui_layout'] == 'narrow': # narrow {{{ + self.book_details = BookDetails(False, self) self.stack = Stack(self) self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.svg'), orientation=Qt.Vertical, parent=self, side_index=1) - self._layout_mem = [QWidget(self), QHBoxLayout()] - self._layout_mem[0].setLayout(self._layout_mem[1]) - l = self._layout_mem[1] - l.addWidget(self.stack) - self.sidebar = SideBar([getattr(self, x+'_splitter') - for x in ('bd', 'tb', 'cb')], self.jobs_button, parent=self) - l.addWidget(self.sidebar) - self.bd_splitter.addWidget(self._layout_mem[0]) - self.bd_splitter.addWidget(self.status_bar) + self.bd_splitter.addWidget(self.stack) + self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.centralwidget.layout().addWidget(self.bd_splitter) - else: - self.status_bar = HStatusBar(self) - self.setStatusBar(self.status_bar) + # }}} + else: # wide {{{ self.bd_splitter = Splitter('book_details_splitter', _('Book Details'), I('book.svg'), initial_side_size=200, orientation=Qt.Horizontal, parent=self, side_index=1) self.stack = Stack(self) self.bd_splitter.addWidget(self.stack) - self.book_details = BookDetails(self) + self.book_details = BookDetails(True, self) self.bd_splitter.addWidget(self.book_details) self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False) self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.centralwidget.layout().addWidget(self.bd_splitter) + # }}} - for x in ('cb', 'tb', 'bd'): - button = getattr(self, x+'_splitter').button - button.setIconSize(QSize(22, 22)) - self.status_bar.addPermanentWidget(button) - self.status_bar.addPermanentWidget(self.jobs_button) + self.status_bar = StatusBar(self) + for x in ('cb', 'tb', 'bd'): + button = getattr(self, x+'_splitter').button + button.setIconSize(QSize(24, 24)) + self.status_bar.addPermanentWidget(button) + self.status_bar.addPermanentWidget(self.jobs_button) + self.setStatusBar(self.status_bar) def finalize_layout(self): + self.status_bar.initialize(self.system_tray_icon) + self.book_details.show_book_info.connect(self.show_book_info) + self.book_details.files_dropped.connect(self.files_dropped_on_book) + self.book_details.open_containing_folder.connect(self.view_folder_for_id) + self.book_details.view_specific_format.connect(self.view_format_by_id) + m = self.library_view.model() if m.rowCount(None) > 0: self.library_view.set_current_row(0) diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py deleted file mode 100644 index 9aa9b8262c..0000000000 --- a/src/calibre/gui2/status.py +++ /dev/null @@ -1,253 +0,0 @@ -__license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' - -import os - -from PyQt4.Qt import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \ - QSizePolicy, QScrollArea, Qt, QSize, pyqtSignal, \ - QPropertyAnimation, QEasingCurve, QDesktopServices, QUrl - - -from calibre import fit_image, preferred_encoding, isosx -from calibre.gui2 import config -from calibre.gui2.widgets import IMAGE_EXTENSIONS -from calibre.gui2.notify import get_notifier -from calibre.ebooks import BOOK_EXTENSIONS -from calibre.library.comments import comments_to_html -from calibre.gui2.book_details import render_rows - -class BookInfoDisplay(QWidget): - - DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS - files_dropped = pyqtSignal(object, object) - - @classmethod - def paths_from_event(cls, event): - ''' - Accept a drop event and return a list of paths that can be read from - and represent files with extensions. - ''' - if event.mimeData().hasFormat('text/uri-list'): - urls = [unicode(u.toLocalFile()) for u in event.mimeData().urls()] - urls = [u for u in urls if os.path.splitext(u)[1] and os.access(u, os.R_OK)] - return [u for u in urls if os.path.splitext(u)[1][1:].lower() in cls.DROPABBLE_EXTENSIONS] - - def dragEnterEvent(self, event): - if int(event.possibleActions() & Qt.CopyAction) + \ - int(event.possibleActions() & Qt.MoveAction) == 0: - return - paths = self.paths_from_event(event) - if paths: - event.acceptProposedAction() - - def dropEvent(self, event): - paths = self.paths_from_event(event) - event.setDropAction(Qt.CopyAction) - self.files_dropped.emit(event, paths) - - def dragMoveEvent(self, event): - event.acceptProposedAction() - - - class BookCoverDisplay(QLabel): # {{{ - - def __init__(self, coverpath=I('book.svg')): - QLabel.__init__(self) - self.animation = QPropertyAnimation(self, 'size', self) - self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo)) - self.animation.setDuration(1000) - self.animation.setStartValue(QSize(0, 0)) - self.setMaximumWidth(81) - self.setMaximumHeight(108) - self.default_pixmap = QPixmap(coverpath) - self.setScaledContents(True) - self.statusbar_height = 120 - self.setPixmap(self.default_pixmap) - - def do_layout(self): - self.animation.stop() - pixmap = self.pixmap() - pwidth, pheight = pixmap.width(), pixmap.height() - width, height = fit_image(pwidth, pheight, - pwidth, self.statusbar_height-20)[1:] - self.setMaximumHeight(height) - try: - aspect_ratio = pwidth/float(pheight) - except ZeroDivisionError: - aspect_ratio = 1 - self.setMaximumWidth(int(aspect_ratio*self.maximumHeight())) - self.animation.setEndValue(self.maximumSize()) - - def setPixmap(self, pixmap): - QLabel.setPixmap(self, pixmap) - self.do_layout() - self.animation.start() - - def sizeHint(self): - return QSize(self.maximumWidth(), self.maximumHeight()) - - def relayout(self, statusbar_size): - self.statusbar_height = statusbar_size.height() - self.do_layout() - - # }}} - - class BookDataDisplay(QLabel): - - mr = pyqtSignal(object) - link_clicked = pyqtSignal(object) - - def __init__(self): - QLabel.__init__(self) - self.setText('') - self.setWordWrap(True) - self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) - self.linkActivated.connect(self.link_activated) - self._link_clicked = False - - def mouseReleaseEvent(self, ev): - QLabel.mouseReleaseEvent(self, ev) - if not self._link_clicked: - self.mr.emit(ev) - self._link_clicked = False - - def link_activated(self, link): - self._link_clicked = True - link = unicode(link) - self.link_clicked.emit(link) - - show_book_info = pyqtSignal() - - def __init__(self, clear_message): - QWidget.__init__(self) - self.setCursor(Qt.PointingHandCursor) - self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) - self._layout = QHBoxLayout() - self.setLayout(self._layout) - self.clear_message = clear_message - self.cover_display = BookInfoDisplay.BookCoverDisplay() - self._layout.addWidget(self.cover_display) - self.book_data = BookInfoDisplay.BookDataDisplay() - self.book_data.mr.connect(self.mouseReleaseEvent) - self._layout.addWidget(self.book_data) - self.data = {} - self.setVisible(False) - self._layout.setAlignment(self.cover_display, Qt.AlignTop|Qt.AlignLeft) - - def mouseReleaseEvent(self, ev): - ev.accept() - self.show_book_info.emit() - - def show_data(self, data): - if data.has_key('cover'): - self.cover_display.setPixmap(QPixmap.fromImage(data.pop('cover'))) - else: - self.cover_display.setPixmap(self.cover_display.default_pixmap) - - rows, comments = [], '' - self.book_data.setText('') - self.data = data.copy() - rows = render_rows(self.data) - rows = '\n'.join([u'%s:%s'%(k,t) for - k, t in rows]) - if _('Comments') in self.data: - comments = comments_to_html(self.data[_('Comments')]) - comments = ('%s:'%_('Comments'))+comments - left_pane = u'%s
'%rows - right_pane = u'
%s
'%comments - self.book_data.setText(u'
%s%s
' - % (left_pane, right_pane)) - - self.clear_message() - self.book_data.updateGeometry() - self.updateGeometry() - self.setVisible(True) - self.setToolTip('

'+_('Click to open Book Details window') + - '

' + _('Path') + ': ' + data.get(_('Path'), '')) - - - -class StatusBarInterface(object): - - def initialize(self, systray=None): - self.systray = systray - self.notifier = get_notifier(systray) - - def show_message(self, msg, timeout=0): - QStatusBar.showMessage(self, msg, timeout) - if self.notifier is not None and not config['disable_tray_notification']: - if isosx and isinstance(msg, unicode): - try: - msg = msg.encode(preferred_encoding) - except UnicodeEncodeError: - msg = msg.encode('utf-8') - self.notifier(msg) - - def clear_message(self): - QStatusBar.clearMessage(self) - -class BookDetailsInterface(object): - - # These signals must be defined in the class implementing this interface - files_dropped = None - show_book_info = None - open_containing_folder = None - view_specific_format = None - - def reset_info(self): - raise NotImplementedError() - - def show_data(self, data): - raise NotImplementedError() - -class HStatusBar(QStatusBar, StatusBarInterface): - pass - -class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface): - - files_dropped = pyqtSignal(object, object) - show_book_info = pyqtSignal() - open_containing_folder = pyqtSignal(int) - view_specific_format = pyqtSignal(int, object) - - resized = pyqtSignal(object) - - def initialize(self, systray=None): - StatusBarInterface.initialize(self, systray=systray) - self.book_info = BookInfoDisplay(self.clear_message) - self.book_info.setAcceptDrops(True) - self.scroll_area = QScrollArea() - self.scroll_area.setWidget(self.book_info) - self.scroll_area.setWidgetResizable(True) - self.book_info.show_book_info.connect(self.show_book_info.emit, - type=Qt.QueuedConnection) - self.book_info.files_dropped.connect(self.files_dropped.emit, - type=Qt.QueuedConnection) - self.book_info.book_data.link_clicked.connect(self._link_clicked) - self.addWidget(self.scroll_area, 100) - self.setMinimumHeight(120) - self.resized.connect(self.book_info.cover_display.relayout) - self.book_info.cover_display.relayout(self.size()) - - - def _link_clicked(self, link): - typ, _, val = link.partition(':') - if typ == 'path': - self.open_containing_folder.emit(int(val)) - elif typ == 'format': - id_, fmt = val.split(':') - self.view_specific_format.emit(int(id_), fmt) - elif typ == 'devpath': - QDesktopServices.openUrl(QUrl.fromLocalFile(val)) - - - def resizeEvent(self, ev): - self.resized.emit(self.size()) - - def reset_info(self): - self.book_info.show_data({}) - - def show_data(self, data): - self.book_info.show_data(data) - diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 2226520cf2..aa2d94a637 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -126,8 +126,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{ # Jobs Button {{{ self.job_manager = JobManager() self.jobs_dialog = JobsDialog(self, self.job_manager) - self.jobs_button = JobsButton(horizontal=config['gui_layout'] != - 'narrow') + self.jobs_button = JobsButton(horizontal=True) self.jobs_button.initialize(self.jobs_dialog, self.job_manager) # }}} @@ -216,12 +215,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{ self.vanity.setText(self.vanity_template%dict(version=' ', device=' ')) self.device_info = ' ' UpdateMixin.__init__(self, opts) - ####################### Status Bar ##################### - self.status_bar.initialize(self.system_tray_icon) - self.book_details.show_book_info.connect(self.show_book_info) - self.book_details.files_dropped.connect(self.files_dropped_on_book) - self.book_details.open_containing_folder.connect(self.view_folder_for_id) - self.book_details.view_specific_format.connect(self.view_format_by_id) ####################### Setup Toolbar ##################### ToolbarMixin.__init__(self)