Merge from trunk

This commit is contained in:
kiwidude 2011-06-14 20:30:00 +01:00
commit ff73b5a26c
8 changed files with 128 additions and 61 deletions

View File

@ -95,6 +95,11 @@ void launch_calibre(LPCTSTR exe, LPCTSTR config_dir, LPCTSTR library_dir) {
ExitProcess(1); ExitProcess(1);
} }
if (! SetEnvironmentVariable(TEXT("CALIBRE_PORTABLE_BUILD"), exe)) {
show_last_error(TEXT("Failed to set environment variables"));
ExitProcess(1);
}
dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP; dwFlags = CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP;
_sntprintf_s(cmdline, BUFSIZE, _TRUNCATE, TEXT(" \"--with-library=%s\""), library_dir); _sntprintf_s(cmdline, BUFSIZE, _TRUNCATE, TEXT(" \"--with-library=%s\""), library_dir);

View File

@ -32,6 +32,7 @@ isbsd = isfreebsd or isnetbsd
islinux = not(iswindows or isosx or isbsd) islinux = not(iswindows or isosx or isbsd)
isfrozen = hasattr(sys, 'frozen') isfrozen = hasattr(sys, 'frozen')
isunix = isosx or islinux isunix = isosx or islinux
isportable = os.environ.get('CALIBRE_PORTABLE_BUILD', None) is not None
try: try:
preferred_encoding = locale.getpreferredencoding() preferred_encoding = locale.getpreferredencoding()

View File

@ -493,6 +493,8 @@ def initialize_plugin(plugin, path_to_zip_file):
raise InvalidPlugin((_('Initialization of plugin %s failed with traceback:') raise InvalidPlugin((_('Initialization of plugin %s failed with traceback:')
%tb) + '\n'+tb) %tb) + '\n'+tb)
def has_external_plugins():
return bool(config['plugins'])
def initialize_plugins(): def initialize_plugins():
global _initialized_plugins global _initialized_plugins

View File

@ -14,11 +14,11 @@ from PyQt4.Qt import (Qt, QUrl, QFrame, QVBoxLayout, QLabel, QBrush, QTextEdit,
QAbstractTableModel, QVariant, QTableView, QModelIndex, QAbstractTableModel, QVariant, QTableView, QModelIndex,
QSortFilterProxyModel, pyqtSignal, QAction, QIcon, QDialog, QSortFilterProxyModel, pyqtSignal, QAction, QIcon, QDialog,
QFont, QPixmap, QSize) QFont, QPixmap, QSize)
from PyQt4 import QtCore
from calibre import browser, prints from calibre import browser, prints
from calibre.constants import numeric_version, iswindows, isosx, DEBUG from calibre.constants import numeric_version, iswindows, isosx, DEBUG
from calibre.customize.ui import (initialized_plugins, is_disabled, remove_plugin, from calibre.customize.ui import (initialized_plugins, is_disabled, remove_plugin,
add_plugin, enable_plugin, disable_plugin, NameConflict) add_plugin, enable_plugin, disable_plugin,
NameConflict, has_external_plugins)
from calibre.gui2 import error_dialog, question_dialog, info_dialog, NONE, open_url, gprefs from calibre.gui2 import error_dialog, question_dialog, info_dialog, NONE, open_url, gprefs
from calibre.gui2.preferences.plugins import ConfigWidget from calibre.gui2.preferences.plugins import ConfigWidget
from calibre.utils.date import UNDEFINED_DATE, format_date from calibre.utils.date import UNDEFINED_DATE, format_date
@ -39,6 +39,8 @@ def get_plugin_updates_available():
Returns None if no updates found Returns None if no updates found
Returns list(DisplayPlugin) of plugins installed that have a new version Returns list(DisplayPlugin) of plugins installed that have a new version
''' '''
if not has_external_plugins():
return None
display_plugins = read_available_plugins() display_plugins = read_available_plugins()
if display_plugins: if display_plugins:
update_plugins = filter(filter_upgradeable_plugins, display_plugins) update_plugins = filter(filter_upgradeable_plugins, display_plugins)
@ -477,10 +479,12 @@ class PluginUpdaterDialog(SizePersistedDialog):
self.resize_dialog() self.resize_dialog()
def _initialize_controls(self): def _initialize_controls(self):
self.setWindowTitle(_('Check for user plugin updates')) self.setWindowTitle(_('User plugins'))
self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
layout = QVBoxLayout(self) layout = QVBoxLayout(self)
self.setLayout(layout) self.setLayout(layout)
title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png', _('User Plugin Status')) title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
_('User Plugins'))
layout.addLayout(title_layout) layout.addLayout(title_layout)
header_layout = QHBoxLayout() header_layout = QHBoxLayout()
@ -498,7 +502,7 @@ class PluginUpdaterDialog(SizePersistedDialog):
self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection) self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection)
self.plugin_view.setAlternatingRowColors(True) self.plugin_view.setAlternatingRowColors(True)
self.plugin_view.setSortingEnabled(True) self.plugin_view.setSortingEnabled(True)
self.plugin_view.setIconSize(QtCore.QSize(28, 28)) self.plugin_view.setIconSize(QSize(28, 28))
layout.addWidget(self.plugin_view) layout.addWidget(self.plugin_view)
details_layout = QHBoxLayout() details_layout = QHBoxLayout()

View File

@ -27,7 +27,6 @@ def partial(*args, **kwargs):
_keep_refs.append(ans) _keep_refs.append(ans)
return ans return ans
class LibraryViewMixin(object): # {{{ class LibraryViewMixin(object): # {{{
def __init__(self, db): def __init__(self, db):
@ -145,6 +144,7 @@ class UpdateLabel(QLabel): # {{{
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
QLabel.__init__(self, *args, **kwargs) QLabel.__init__(self, *args, **kwargs)
self.setCursor(Qt.PointingHandCursor)
def contextMenuEvent(self, e): def contextMenuEvent(self, e):
pass pass
@ -182,14 +182,6 @@ class StatusBar(QStatusBar): # {{{
self.defmsg.setText(self.default_message) self.defmsg.setText(self.default_message)
self.clearMessage() self.clearMessage()
def new_version_available(self, ver, url):
msg = (u'<span style="color:red; font-weight: bold">%s: <a'
' href="update:%s">%s<a></span>') % (
_('Update found'), ver, ver)
self.update_label.setText(msg)
self.update_label.setCursor(Qt.PointingHandCursor)
self.update_label.setVisible(True)
def get_version(self): def get_version(self):
dv = os.environ.get('CALIBRE_DEVELOP_FROM', None) dv = os.environ.get('CALIBRE_DEVELOP_FROM', None)
v = __version__ v = __version__
@ -257,12 +249,6 @@ class LayoutMixin(object): # {{{
self.setStatusBar(self.status_bar) self.setStatusBar(self.status_bar)
self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)
def update_link_clicked(self, url):
url = unicode(url)
if url.startswith('update:'):
version = url.partition(':')[-1]
self.update_found(version, force=True)
def finalize_layout(self): def finalize_layout(self):
self.status_bar.initialize(self.system_tray_icon) self.status_bar.initialize(self.system_tray_icon)
self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info)

View File

@ -7,18 +7,17 @@ __docformat__ = 'restructuredtext en'
import textwrap, os import textwrap, os
from collections import OrderedDict from collections import OrderedDict
from functools import partial
from PyQt4.Qt import Qt, QModelIndex, QAbstractItemModel, QVariant, QIcon, \ from PyQt4.Qt import (Qt, QModelIndex, QAbstractItemModel, QVariant, QIcon,
QBrush QBrush)
from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.gui2.preferences.plugins_ui import Ui_Form from calibre.gui2.preferences.plugins_ui import Ui_Form
from calibre.customize.ui import (initialized_plugins, is_disabled, enable_plugin, from calibre.customize.ui import (initialized_plugins, is_disabled, enable_plugin,
disable_plugin, plugin_customization, add_plugin, disable_plugin, plugin_customization, add_plugin,
remove_plugin, NameConflict) remove_plugin, NameConflict)
from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files, \ from calibre.gui2 import (NONE, error_dialog, info_dialog, choose_files,
question_dialog, gprefs question_dialog, gprefs)
from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.icu import lower from calibre.utils.icu import lower
@ -218,8 +217,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.customize_plugin_button.clicked.connect(self.customize_plugin) self.customize_plugin_button.clicked.connect(self.customize_plugin)
self.remove_plugin_button.clicked.connect(self.remove_plugin) self.remove_plugin_button.clicked.connect(self.remove_plugin)
self.button_plugin_add.clicked.connect(self.add_plugin) self.button_plugin_add.clicked.connect(self.add_plugin)
self.button_plugin_updates.clicked.connect(partial(self.update_plugins, not_installed=False)) self.button_plugin_updates.clicked.connect(self.update_plugins)
self.button_plugin_new.clicked.connect(partial(self.update_plugins, not_installed=True)) self.button_plugin_new.clicked.connect(self.get_plugins)
self.search.initialize('plugin_search_history', self.search.initialize('plugin_search_history',
help_text=_('Search for plugin')) help_text=_('Search for plugin'))
self.search.search.connect(self.find) self.search.search.connect(self.find)
@ -356,6 +355,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
plugin.name + _(' cannot be removed. It is a ' plugin.name + _(' cannot be removed. It is a '
'builtin plugin. Try disabling it instead.')).exec_() 'builtin plugin. Try disabling it instead.')).exec_()
def get_plugins(self):
self.update_plugins(not_installed=True)
def update_plugins(self, not_installed=False): def update_plugins(self, not_installed=False):
from calibre.gui2.dialogs.plugin_updater import (PluginUpdaterDialog, from calibre.gui2.dialogs.plugin_updater import (PluginUpdaterDialog,
FILTER_UPDATE_AVAILABLE, FILTER_NOT_INSTALLED) FILTER_UPDATE_AVAILABLE, FILTER_NOT_INSTALLED)

View File

@ -112,6 +112,13 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::HLine</enum>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item> <item>
@ -128,7 +135,7 @@
<item> <item>
<widget class="QPushButton" name="button_plugin_updates"> <widget class="QPushButton" name="button_plugin_updates">
<property name="text"> <property name="text">
<string>&amp;Check for plugin updates</string> <string>Check for &amp;updated plugins</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">
@ -139,7 +146,7 @@
<item> <item>
<widget class="QPushButton" name="button_plugin_new"> <widget class="QPushButton" name="button_plugin_new">
<property name="text"> <property name="text">
<string>&amp;Get new plugins</string> <string>Get &amp;new plugins</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../../resources/images.qrc"> <iconset resource="../../../../resources/images.qrc">

View File

@ -3,22 +3,34 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import traceback import traceback
from PyQt4.Qt import QThread, pyqtSignal, Qt, QUrl, QDialog, QGridLayout, \ from PyQt4.Qt import (QThread, pyqtSignal, Qt, QUrl, QDialog, QGridLayout,
QLabel, QCheckBox, QDialogButtonBox, QIcon, QPixmap QLabel, QCheckBox, QDialogButtonBox, QIcon, QPixmap)
import mechanize import mechanize
from calibre.constants import __appname__, __version__, iswindows, isosx from calibre.constants import (__appname__, __version__, iswindows, isosx,
isportable)
from calibre import browser from calibre import browser
from calibre.utils.config import prefs from calibre.utils.config import prefs
from calibre.gui2 import config, dynamic, open_url from calibre.gui2 import config, dynamic, open_url
from calibre.gui2.dialogs.plugin_updater import get_plugin_updates_available from calibre.gui2.dialogs.plugin_updater import get_plugin_updates_available
URL = 'http://status.calibre-ebook.com/latest' URL = 'http://status.calibre-ebook.com/latest'
NO_CALIBRE_UPDATE = '-0.0.0'
VSEP = '|'
def get_newest_version():
br = browser()
req = mechanize.Request(URL)
req.add_header('CALIBRE_VERSION', __version__)
req.add_header('CALIBRE_OS',
'win' if iswindows else 'osx' if isosx else 'oth')
req.add_header('CALIBRE_INSTALL_UUID', prefs['installation_uuid'])
version = br.open(req).read().strip()
return version
class CheckForUpdates(QThread): class CheckForUpdates(QThread):
update_found = pyqtSignal(object) update_found = pyqtSignal(object)
plugin_update_found = pyqtSignal(object)
INTERVAL = 24*60*60 INTERVAL = 24*60*60
def __init__(self, parent): def __init__(self, parent):
@ -26,29 +38,29 @@ class CheckForUpdates(QThread):
def run(self): def run(self):
while True: while True:
calibre_update_version = NO_CALIBRE_UPDATE
plugins_update_found = 0
try: try:
br = browser() version = get_newest_version()
req = mechanize.Request(URL)
req.add_header('CALIBRE_VERSION', __version__)
req.add_header('CALIBRE_OS',
'win' if iswindows else 'osx' if isosx else 'oth')
req.add_header('CALIBRE_INSTALL_UUID', prefs['installation_uuid'])
version = br.open(req).read().strip()
if version and version != __version__ and len(version) < 10: if version and version != __version__ and len(version) < 10:
self.update_found.emit(version) calibre_update_version = version
except: except:
traceback.print_exc() traceback.print_exc()
try: try:
update_plugins = get_plugin_updates_available() update_plugins = get_plugin_updates_available()
if update_plugins: if update_plugins is not None:
self.plugin_update_found.emit(update_plugins) plugins_update_found = len(update_plugins)
except: except:
traceback.print_exc() traceback.print_exc()
if (calibre_update_version != NO_CALIBRE_UPDATE or
plugins_update_found > 0):
self.update_found.emit('%s%s%d'%(calibre_update_version,
VSEP, plugins_update_found))
self.sleep(self.INTERVAL) self.sleep(self.INTERVAL)
class UpdateNotification(QDialog): class UpdateNotification(QDialog):
def __init__(self, version, parent=None): def __init__(self, calibre_version, plugin_updates, parent=None):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
self.resize(400, 250) self.resize(400, 250)
self.l = QGridLayout() self.l = QGridLayout()
@ -62,7 +74,8 @@ class UpdateNotification(QDialog):
'See the <a href="http://calibre-ebook.com/whats-new' 'See the <a href="http://calibre-ebook.com/whats-new'
'">new features</a>.') + '<p>'+_('Update <b>only</b> if one of the ' '">new features</a>.') + '<p>'+_('Update <b>only</b> if one of the '
'new features or bug fixes is important to you. ' 'new features or bug fixes is important to you. '
'If the current version works well for you, do not update.'))%(__appname__, version)) 'If the current version works well for you, do not update.'))%(
__appname__, calibre_version))
self.label.setOpenExternalLinks(True) self.label.setOpenExternalLinks(True)
self.label.setWordWrap(True) self.label.setWordWrap(True)
self.setWindowTitle(_('Update available!')) self.setWindowTitle(_('Update available!'))
@ -78,18 +91,30 @@ class UpdateNotification(QDialog):
b = self.bb.addButton(_('&Get update'), self.bb.AcceptRole) b = self.bb.addButton(_('&Get update'), self.bb.AcceptRole)
b.setDefault(True) b.setDefault(True)
b.setIcon(QIcon(I('arrow-down.png'))) b.setIcon(QIcon(I('arrow-down.png')))
if plugin_updates > 0:
b = self.bb.addButton(_('Update &plugins'), self.bb.ActionRole)
b.setIcon(QIcon(I('plugins/plugin_updater.png')))
b.clicked.connect(self.get_plugins, type=Qt.QueuedConnection)
self.bb.addButton(self.bb.Cancel) self.bb.addButton(self.bb.Cancel)
self.l.addWidget(self.bb, 2, 0, 1, -1) self.l.addWidget(self.bb, 2, 0, 1, -1)
self.bb.accepted.connect(self.accept) self.bb.accepted.connect(self.accept)
self.bb.rejected.connect(self.reject) self.bb.rejected.connect(self.reject)
dynamic.set('update to version %s'%version, False) dynamic.set('update to version %s'%calibre_version, False)
def get_plugins(self):
from calibre.gui2.dialogs.plugin_updater import (PluginUpdaterDialog,
FILTER_UPDATE_AVAILABLE)
d = PluginUpdaterDialog(self.parent(),
initial_filter=FILTER_UPDATE_AVAILABLE)
d.exec_()
def show_future(self, *args): def show_future(self, *args):
config.set('new_version_notification', bool(self.cb.isChecked())) config.set('new_version_notification', bool(self.cb.isChecked()))
def accept(self): def accept(self):
url = 'http://calibre-ebook.com/download_'+\ url = ('http://calibre-ebook.com/download_' +
('windows' if iswindows else 'osx' if isosx else 'linux') ('portable' if isportable else 'windows' if iswindows
else 'osx' if isosx else 'linux'))
open_url(QUrl(url)) open_url(QUrl(url))
QDialog.accept(self) QDialog.accept(self)
@ -101,33 +126,68 @@ class UpdateMixin(object):
self.update_checker = CheckForUpdates(self) self.update_checker = CheckForUpdates(self)
self.update_checker.update_found.connect(self.update_found, self.update_checker.update_found.connect(self.update_found,
type=Qt.QueuedConnection) type=Qt.QueuedConnection)
self.update_checker.plugin_update_found.connect(self.plugin_update_found,
type=Qt.QueuedConnection)
self.update_checker.start() self.update_checker.start()
def update_found(self, version, force=False): def update_found(self, version, force=False):
os = 'windows' if iswindows else 'osx' if isosx else 'linux' try:
url = 'http://calibre-ebook.com/download_%s'%os calibre_version, plugin_updates = version.split(VSEP)
self.status_bar.new_version_available(version, url) plugin_updates = int(plugin_updates)
except:
traceback.print_exc()
return
has_calibre_update = calibre_version and calibre_version != NO_CALIBRE_UPDATE
has_plugin_updates = plugin_updates > 0
if not has_calibre_update and not has_plugin_updates:
self.status_bar.update_label.setVisible(False)
return
if has_calibre_update:
plt = u''
if has_plugin_updates:
plt = ' (%d plugin updates)'%plugin_updates
msg = (u'<span style="color:red; font-weight: bold">%s: '
u'<a href="update:%s">%s%s</a></span>') % (
_('Update found'), version, calibre_version, plt)
else:
msg = (u'<a href="update:%s">%d %s</a>')%(version, plugin_updates,
_('updated plugins'))
self.status_bar.update_label.setText(msg)
self.status_bar.update_label.setVisible(True)
if force or (config.get('new_version_notification') and \ if has_plugin_updates:
dynamic.get('update to version %s'%version, True)): self.plugin_update_found(plugin_updates)
self._update_notification__ = UpdateNotification(version,
parent=self)
self._update_notification__.show()
def plugin_update_found(self, updates, icon_only=False): if has_calibre_update:
if force or (config.get('new_version_notification') and \
dynamic.get('update to version %s'%calibre_version, True)):
self._update_notification__ = UpdateNotification(calibre_version,
plugin_updates, parent=self)
self._update_notification__.show()
elif has_plugin_updates:
if force:
from calibre.gui2.dialogs.plugin_updater import (PluginUpdaterDialog,
FILTER_UPDATE_AVAILABLE)
d = PluginUpdaterDialog(self,
initial_filter=FILTER_UPDATE_AVAILABLE)
d.exec_()
def plugin_update_found(self, number_of_updates):
# Change the plugin icon to indicate there are updates available # Change the plugin icon to indicate there are updates available
plugin = self.iactions.get('Plugin Updates', None) plugin = self.iactions.get('Plugin Updates', None)
if not plugin: if not plugin:
return return
if updates: if number_of_updates:
plugin.qaction.setText(_('Plugin Updates')+'*') plugin.qaction.setText(_('Plugin Updates')+'*')
plugin.qaction.setIcon(QIcon(I('plugins/plugin_updater_updates.png'))) plugin.qaction.setIcon(QIcon(I('plugins/plugin_updater_updates.png')))
plugin.qaction.setToolTip(_('There are %d plugin updates available')%len(updates)) plugin.qaction.setToolTip(
_('There are %d plugin updates available')%number_of_updates)
else: else:
plugin.qaction.setText(_('Plugin Updates')) plugin.qaction.setText(_('Plugin Updates'))
plugin.qaction.setIcon(QIcon(I('plugins/plugin_updater.png'))) plugin.qaction.setIcon(QIcon(I('plugins/plugin_updater.png')))
plugin.qaction.setToolTip(_('Install and configure user plugins')) plugin.qaction.setToolTip(_('Install and configure user plugins'))
def update_link_clicked(self, url):
url = unicode(url)
if url.startswith('update:'):
version = url[len('update:'):]
self.update_found(version, force=True)