From 38417a67a39500ea9789f183bfa2242572b40507 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 8 Jun 2008 14:25:43 -0700 Subject: [PATCH] IGN:Use QDesktopServices for desktop integration --- Makefile | 6 +- src/calibre/__init__.py | 13 +- src/calibre/gui2/dialogs/user_profiles.py | 21 +- src/calibre/gui2/main.py | 6 +- .../trac/plugins/templates/pyinstaller.html | 2 +- src/calibre/utils/__init__.py | 39 --- src/calibre/utils/simplemapi.py | 262 ------------------ upload.py | 33 +-- 8 files changed, 41 insertions(+), 341 deletions(-) delete mode 100644 src/calibre/utils/simplemapi.py diff --git a/Makefile b/Makefile index e65fd0d235..c9f7ca1db4 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,8 @@ pictureflow : pot : cd src/calibre/translations && ${PYTHON} __init__.py pot -egg : all - ${PYTHON} setup.py register bdist_egg --exclude-source-files upload +egg : + ${PYTHON} setup.py bdist_egg --exclude-source-files +linux_binary: + ${PYTHON} -c "import upload; upload._build_linux()" diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 3bfda762a0..23befd12dd 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -16,7 +16,8 @@ from optparse import IndentedHelpFormatter from logging import Formatter from ttfquery import findsystem, describe -from PyQt4.QtCore import QSettings, QVariant +from PyQt4.QtCore import QSettings, QVariant, QUrl +from PyQt4.QtGui import QDesktopServices from calibre.translations.msgfmt import make from calibre.ebooks.chardet import detect @@ -389,13 +390,9 @@ def detect_ncpus(): def launch(path_or_url): - if islinux: - subprocess.Popen(('xdg-open', path_or_url)) - elif isosx: - subprocess.Popen(('open', path_or_url)) - elif iswindows: - win32api = __import__('win32api', globals(), locals(), [], -1) - win32api.ShellExecute(0, 'open', path_or_url, None, os.getcwd(), 1) + if os.path.exists(path_or_url): + path_or_url = 'file:'+path_or_url + QDesktopServices.openUrl(QUrl(path_or_url)) def relpath(target, base=os.curdir): """ diff --git a/src/calibre/gui2/dialogs/user_profiles.py b/src/calibre/gui2/dialogs/user_profiles.py index 09c4c971bf..5cae63c6d7 100644 --- a/src/calibre/gui2/dialogs/user_profiles.py +++ b/src/calibre/gui2/dialogs/user_profiles.py @@ -1,17 +1,15 @@ -from calibre.gui2 import choose_files __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' import time, os -from PyQt4.QtCore import SIGNAL -from PyQt4.QtGui import QDialog, QMessageBox +from PyQt4.QtCore import SIGNAL, QUrl +from PyQt4.QtGui import QDialog, QMessageBox, QDesktopServices from calibre.web.feeds.recipes import compile_recipe from calibre.web.feeds.news import AutomaticNewsRecipe from calibre.gui2.dialogs.user_profiles_ui import Ui_Dialog -from calibre.gui2 import qstring_to_unicode, error_dialog, question_dialog +from calibre.gui2 import qstring_to_unicode, error_dialog, question_dialog, choose_files from calibre.gui2.widgets import PythonHighlighter -from calibre.utils import sendmail from calibre.ptempfile import PersistentTemporaryFile from calibre import isosx @@ -68,10 +66,13 @@ class UserProfiles(QDialog, Ui_Dialog): pt = PersistentTemporaryFile(suffix='.py') pt.write(src.encode('utf-8')) pt.close() - sendmail(subject='Recipe for '+title, - attachments=[pt.name], - body=_('Save the text below into a file named recipe.py and send the file to your friends, to allow them to use this recipe.') if isosx else _('The attached file: %s is a recipe to download %s.')%(os.path.basename(pt.name), title)) - + body = _('The attached file: %s is a recipe to download %s.')%(os.path.basename(pt.name), title) + subject = _('Recipe for ')+title + url = QUrl('mailto:') + url.addQueryItem('subject', subject) + url.addQueryItem('body', body) + url.addQueryItem('attachment', pt.name) + QDesktopServices.openUrl(url) def edit_profile(self, current, previous): @@ -224,4 +225,4 @@ class %(classname)s(%(base_class)s): for i in self.available_profiles.items(): yield i.user_data - \ No newline at end of file + diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index eb631398ad..a760a7d4a3 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -3,9 +3,9 @@ __copyright__ = '2008, Kovid Goyal ' import os, sys, textwrap, collections, traceback, shutil, time from xml.parsers.expat import ExpatError from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \ - QVariant, QThread, QString, QSize + QVariant, QThread, QString, QSize, QUrl from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \ - QToolButton, QDialog, QSizePolicy + QToolButton, QDialog, QSizePolicy, QDesktopServices from PyQt4.QtSvg import QSvgRenderer from calibre import __version__, __appname__, islinux, sanitize_file_name, launch, \ @@ -858,7 +858,7 @@ class Main(MainWindow, Ui_MainWindow): monitor=False) self.viewer_job_id += 1 else: - launch(name) + QDesktopServices.openUrl(QUrl('file:'+name))#launch(name) time.sleep(2) # User feedback def view_specific_format(self, triggered): diff --git a/src/calibre/trac/plugins/templates/pyinstaller.html b/src/calibre/trac/plugins/templates/pyinstaller.html index 4b77e75511..c04fd13da4 100644 --- a/src/calibre/trac/plugins/templates/pyinstaller.html +++ b/src/calibre/trac/plugins/templates/pyinstaller.html @@ -16,7 +16,7 @@

Download $app for Linux

This binary package is compatible with most recent linux distributions running on Intel 32 bit CPUs.

- + (Version: $version Changelog)

To install, copy paste the following command into a terminal and press Enter: diff --git a/src/calibre/utils/__init__.py b/src/calibre/utils/__init__.py index b109afa546..48f75c0428 100644 --- a/src/calibre/utils/__init__.py +++ b/src/calibre/utils/__init__.py @@ -6,43 +6,4 @@ __docformat__ = 'restructuredtext en' ''' Miscelleaneous utilities. ''' -import subprocess -from calibre import iswindows, isosx -from calibre.ptempfile import PersistentTemporaryFile - -def sendmail(recipient='', subject='', attachments=[], body=''): - if not recipient: - recipient = 'someone@somewhere.net' - if isinstance(recipient, unicode): - recipient = recipient.encode('utf-8') - if isinstance(subject, unicode): - subject = subject.encode('utf-8') - if isinstance(body, unicode): - body = body.encode('utf-8') - for i, src in enumerate(attachments): - if isinstance(src, unicode): - attachments[i] = src.encode('utf-8') - - if iswindows: - from calibre.utils.simplemapi import SendMail - SendMail(recipient, subject=subject, body=body, attachfiles=';'.join(attachments)) - elif isosx: - pt = PersistentTemporaryFile(suffix='.txt') - pt.write(body) - if attachments: - pt.write('\n\n----\n\n') - pt.write(open(attachments[0], 'rb').read()) - pt.close() - - subprocess.Popen(('open', '-t', pt.name)) - - else: - body = '"' + body.replace('"', '\\"') + '"' - subject = '"' + subject.replace('"', '\\"') + '"' - attach = '' - if attachments: - attach = attachments[0] - attach = '"' + attach.replace('"', '\\"') + '"' - attach = '--attach '+attach - subprocess.check_call('xdg-email --utf8 --subject %s --body %s %s %s'%(subject, body, attach, recipient), shell=True) \ No newline at end of file diff --git a/src/calibre/utils/simplemapi.py b/src/calibre/utils/simplemapi.py deleted file mode 100644 index 453ca29db3..0000000000 --- a/src/calibre/utils/simplemapi.py +++ /dev/null @@ -1,262 +0,0 @@ -""" -Copyright (c) 2007 Ian Cook and John Popplewell - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Date : 13 August 2007 -Version : 1.0.2 -Contact : John Popplewell -Email : john@johnnypops.demon.co.uk -Web : http://www.johnnypops.demon.co.uk/python/ -Origin : Based on the original script by Ian Cook - http://www.kirbyfooty.com/simplemapi.py -Comments: Works (and tested) with: - Outlook Express, Outlook 97 and 2000, - Eudora, Incredimail and Mozilla Thunderbird (1.5.0.2) -Thanks : Werner F. Bruhin and Michele Petrazzo on the ctypes list. - Lukas Lalinsky for patches and encouragement. - -If you have any bug-fixes, enhancements or suggestions regarding this -software, please contact me at the above email address. -""" - -import os -from ctypes import * - -FLAGS = c_ulong -LHANDLE = c_ulong -LPLHANDLE = POINTER(LHANDLE) - -# Return codes -SUCCESS_SUCCESS = 0 -MAPI_USER_ABORT = 1 -MAPI_E_USER_ABORT = MAPI_USER_ABORT -MAPI_E_FAILURE = 2 -MAPI_E_LOGON_FAILURE = 3 -MAPI_E_LOGIN_FAILURE = MAPI_E_LOGON_FAILURE -MAPI_E_DISK_FULL = 4 -MAPI_E_INSUFFICIENT_MEMORY = 5 -MAPI_E_ACCESS_DENIED = 6 -MAPI_E_TOO_MANY_SESSIONS = 8 -MAPI_E_TOO_MANY_FILES = 9 -MAPI_E_TOO_MANY_RECIPIENTS = 10 -MAPI_E_ATTACHMENT_NOT_FOUND = 11 -MAPI_E_ATTACHMENT_OPEN_FAILURE = 12 -MAPI_E_ATTACHMENT_WRITE_FAILURE = 13 -MAPI_E_UNKNOWN_RECIPIENT = 14 -MAPI_E_BAD_RECIPTYPE = 15 -MAPI_E_NO_MESSAGES = 16 -MAPI_E_INVALID_MESSAGE = 17 -MAPI_E_TEXT_TOO_LARGE = 18 -MAPI_E_INVALID_SESSION = 19 -MAPI_E_TYPE_NOT_SUPPORTED = 20 -MAPI_E_AMBIGUOUS_RECIPIENT = 21 -MAPI_E_AMBIG_RECIP = MAPI_E_AMBIGUOUS_RECIPIENT -MAPI_E_MESSAGE_IN_USE = 22 -MAPI_E_NETWORK_FAILURE = 23 -MAPI_E_INVALID_EDITFIELDS = 24 -MAPI_E_INVALID_RECIPS = 25 -MAPI_E_NOT_SUPPORTED = 26 -# Recipient class -MAPI_ORIG = 0 -MAPI_TO = 1 -# Send flags -MAPI_LOGON_UI = 1 -MAPI_DIALOG = 8 - -class MapiRecipDesc(Structure): - _fields_ = [ - ('ulReserved', c_ulong), - ('ulRecipClass', c_ulong), - ('lpszName', c_char_p), - ('lpszAddress', c_char_p), - ('ulEIDSize', c_ulong), - ('lpEntryID', c_void_p), - ] -lpMapiRecipDesc = POINTER(MapiRecipDesc) -lppMapiRecipDesc = POINTER(lpMapiRecipDesc) - -class MapiFileDesc(Structure): - _fields_ = [ - ('ulReserved', c_ulong), - ('flFlags', c_ulong), - ('nPosition', c_ulong), - ('lpszPathName', c_char_p), - ('lpszFileName', c_char_p), - ('lpFileType', c_void_p), - ] -lpMapiFileDesc = POINTER(MapiFileDesc) - -class MapiMessage(Structure): - _fields_ = [ - ('ulReserved', c_ulong), - ('lpszSubject', c_char_p), - ('lpszNoteText', c_char_p), - ('lpszMessageType', c_char_p), - ('lpszDateReceived', c_char_p), - ('lpszConversationID', c_char_p), - ('flFlags', FLAGS), - ('lpOriginator', lpMapiRecipDesc), - ('nRecipCount', c_ulong), - ('lpRecips', lpMapiRecipDesc), - ('nFileCount', c_ulong), - ('lpFiles', lpMapiFileDesc), - ] -lpMapiMessage = POINTER(MapiMessage) - -MAPI = windll.mapi32 -MAPISendMail = MAPI.MAPISendMail -MAPISendMail.restype = c_ulong -MAPISendMail.argtypes = (LHANDLE, c_ulong, lpMapiMessage, FLAGS, c_ulong) - -MAPIResolveName = MAPI.MAPIResolveName -MAPIResolveName.restype = c_ulong -MAPIResolveName.argtypes= (LHANDLE, c_ulong, c_char_p, FLAGS, c_ulong, lppMapiRecipDesc) - -MAPIFreeBuffer = MAPI.MAPIFreeBuffer -MAPIFreeBuffer.restype = c_ulong -MAPIFreeBuffer.argtypes = (c_void_p, ) - -MAPILogon = MAPI.MAPILogon -MAPILogon.restype = c_ulong -MAPILogon.argtypes = (LHANDLE, c_char_p, c_char_p, FLAGS, c_ulong, LPLHANDLE) - -MAPILogoff = MAPI.MAPILogoff -MAPILogoff.restype = c_ulong -MAPILogoff.argtypes = (LHANDLE, c_ulong, FLAGS, c_ulong) - - -class MAPIError(WindowsError): - - def __init__(self, code): - WindowsError.__init__(self) - self.code = code - - def __str__(self): - return 'MAPI error %d' % (self.code,) - - -def _logon(profileName=None, password=None): - pSession = LHANDLE() - rc = MAPILogon(0, profileName, password, MAPI_LOGON_UI, 0, byref(pSession)) - if rc != SUCCESS_SUCCESS: - raise MAPIError, rc - return pSession - - -def _logoff(session): - rc = MAPILogoff(session, 0, 0, 0) - if rc != SUCCESS_SUCCESS: - raise MAPIError, rc - - -def _resolveName(session, name): - pRecipDesc = lpMapiRecipDesc() - rc = MAPIResolveName(session, 0, name, 0, 0, byref(pRecipDesc)) - if rc != SUCCESS_SUCCESS: - raise MAPIError, rc - rd = pRecipDesc.contents - name, address = rd.lpszName, rd.lpszAddress - rc = MAPIFreeBuffer(pRecipDesc) - if rc != SUCCESS_SUCCESS: - raise MAPIError, rc - return name, address - - -def _sendMail(session, recipient, subject, body, attach, preview): - nFileCount = len(attach) - if attach: - MapiFileDesc_A = MapiFileDesc * len(attach) - fda = MapiFileDesc_A() - for fd, fa in zip(fda, attach): - fd.ulReserved = 0 - fd.flFlags = 0 - fd.nPosition = -1 - fd.lpszPathName = fa - fd.lpszFileName = None - fd.lpFileType = None - lpFiles = fda - else: - lpFiles = lpMapiFileDesc() - - RecipWork = recipient.split(';') - RecipCnt = len(RecipWork) - MapiRecipDesc_A = MapiRecipDesc * len(RecipWork) - rda = MapiRecipDesc_A() - for rd, ra in zip(rda, RecipWork): - rd.ulReserved = 0 - rd.ulRecipClass = MAPI_TO - try: - rd.lpszName, rd.lpszAddress = _resolveName(session, ra) - except WindowsError: - # work-round for Mozilla Thunderbird - rd.lpszName, rd.lpszAddress = None, ra - rd.ulEIDSize = 0 - rd.lpEntryID = None - recip = rda - - msg = MapiMessage(0, subject, body, None, None, None, 0, lpMapiRecipDesc(), - RecipCnt, recip, - nFileCount, lpFiles) - flags = 0 - if preview: - flags = MAPI_DIALOG - rc = MAPISendMail(session, 0, byref(msg), flags, 0) - if rc != SUCCESS_SUCCESS: - raise MAPIError, rc - - -def SendMail(recipient, subject="", body="", attachfiles="", preview=1): - """Post an e-mail message using Simple MAPI - - recipient - string: address to send to (multiple addresses separated with a semicolon) - subject - string: subject header - body - string: message text - attach - string: files to attach (multiple attachments separated with a semicolon) - preview - bool : if false, minimise user interaction. Default:true - """ - - attach = [] - AttachWork = attachfiles.split(';') - for filename in AttachWork: - if os.path.exists(filename): - attach.append(filename) - attach = map(os.path.abspath, attach) - - restore = os.getcwd() - try: - session = _logon() - try: - _sendMail(session, recipient, subject, body, attach, preview) - finally: - _logoff(session) - finally: - os.chdir(restore) - - -if __name__ == '__main__': - import sys - recipient = "test@johnnypops.demon.co.uk" - subject = "Test Message Subject" - body = "Hi,\r\n\r\nthis is a quick test message,\r\n\r\ncheers,\r\nJohn." - attachment = sys.argv[0] - SendMail(recipient, subject, body, attachment) - - diff --git a/upload.py b/upload.py index 157cce4e7c..00bb2eca08 100644 --- a/upload.py +++ b/upload.py @@ -78,7 +78,7 @@ def build_osx(): return os.path.basename(installer) #return build_installer(installer, vm, 20) -def build_linux(): +def _build_linux(): cwd = os.getcwd() tbz2 = os.path.join(cwd, installer_name('tar.bz2')) SPEC="""\ @@ -125,7 +125,7 @@ open(hook, 'wb').write('hiddenimports = %%s'%%repr(temp) + '\\n') sys.path.insert(0, CALIBRESRC) from calibre.linux import entry_points -executables, scripts = ['calibre_postinstall', 'parallel'], +executables, scripts = ['calibre_postinstall', 'parallel'], \ [os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')] for entry in entry_points['console_scripts'] + entry_points['gui_scripts']: @@ -198,7 +198,7 @@ os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist')) for f in os.listdir('.'): tf.add(f) -"""%('/home/kovid', tbz2) +"""%('/mnt/hgfs/giskard/', tbz2) os.chdir(os.path.expanduser('~/build/pyinstaller')) open('calibre/calibre.spec', 'wb').write(SPEC) try: @@ -207,8 +207,17 @@ for f in os.listdir('.'): os.chdir(cwd) return os.path.basename(tbz2) +def build_linux(): + vm = '/vmware/linux/libprs500-gentoo.vmx' + vmware = ('vmware', '-q', '-x', '-n', vm) + subprocess.Popen(vmware) + print 'Waiting for linux to boot up...' + time.sleep(60) + check_call('ssh linux make -C /mnt/hgfs/giskard/work/calibre all egg linux_binary') + check_call('ssh sudo poweroff') + def build_installers(): - return build_windows(), build_osx() + return build_linux(), build_windows(), build_osx() def upload_demo(): check_call('''html2lrf --title='Demonstration of html2lrf' --author='Kovid Goyal' ''' @@ -232,7 +241,7 @@ def upload_installers(): check_call('''ssh divok rm -f %s/calibre\*.dmg'''%(DOWNLOADS,)) check_call('''scp %s divok:%s/'''%(dmg, DOWNLOADS)) if tbz2 and os.path.exists(tbz2): - check_call('''ssh divok rm -f %s/calibre-\*-i686.tar.bz2'''%(DOWNLOADS,)) + check_call('''ssh divok rm -f %s/calibre-\*-i686.tar.bz2 %s/latest-linux-binary.tar.bz2'''%(DOWNLOADS,DOWNLOADS)) check_call('''scp %s divok:%s/'''%(tbz2, DOWNLOADS)) check_call('''ssh divok ln -s %s/calibre-\*-i686.tar.bz2 %s/latest-linux-binary.tar.bz2'''%(DOWNLOADS,DOWNLOADS)) check_call('''ssh divok chmod a+r %s/\*'''%(DOWNLOADS,)) @@ -261,16 +270,7 @@ def upload_tarball(): check_call('ssh divok rm -f %s/calibre-\*.tar.bz2'%DOWNLOADS) check_call('scp dist/calibre-*.tar.bz2 divok:%s/'%DOWNLOADS) -def pypi(): - vm = '/vmware/linux/libprs500-gentoo.vmx' - vmware = ('vmware', '-q', '-x', '-n', vm) - subprocess.Popen(vmware) - print 'Waiting for linux to boot up...' - time.sleep(60) - check_call('scp ~/.pypirc linux:') - check_call('ssh linux make -C /mnt/hgfs/giskard/work/calibre egg') - check_call('ssh linux rm -f ~/.pypirc') - check_call('ssh linux sudo poweroff') + def main(): upload = len(sys.argv) < 2 @@ -289,10 +289,11 @@ def main(): print 'Uploading installers...' upload_installers() print 'Uploading to PyPI' - pypi() upload_tarball() upload_docs() upload_user_manual() + check_call('rm -f dist/*.bz2 dist/*.exe dist/*.dmg') + check_call('python setup.py register upload') check_call('''rm -rf dist/* build/*''') if __name__ == '__main__':