mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Modernize the code used to interact with macOS
Recycle bin and notifications code. The old recycle bin code was deprecated. Notifications support has been moved in-process.
This commit is contained in:
parent
b5230d9bd8
commit
cff789f12c
@ -53,27 +53,6 @@
|
|||||||
|
|
||||||
#define NUKE(x) Py_XDECREF(x); x = NULL;
|
#define NUKE(x) Py_XDECREF(x); x = NULL;
|
||||||
|
|
||||||
/* This function only works on 10.5 and later. Pass in a unicode object as path */
|
|
||||||
static PyObject* usbobserver_send2trash(PyObject *self, PyObject *args)
|
|
||||||
{
|
|
||||||
UInt8 *utf8_chars;
|
|
||||||
FSRef fp;
|
|
||||||
OSStatus op_result;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "es", "utf-8", &utf8_chars)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FSPathMakeRefWithOptions(utf8_chars, kFSPathMakeRefDoNotFollowLeafSymlink, &fp, NULL);
|
|
||||||
op_result = FSMoveObjectToTrashSync(&fp, NULL, kFSFileOperationDefaultOptions);
|
|
||||||
PyMem_Free(utf8_chars);
|
|
||||||
if (op_result != noErr) {
|
|
||||||
PyErr_SetString(PyExc_OSError, GetMacOSStatusCommentString(op_result));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
usbobserver_get_iokit_string_property(io_service_t dev, CFStringRef prop) {
|
usbobserver_get_iokit_string_property(io_service_t dev, CFStringRef prop) {
|
||||||
@ -469,9 +448,6 @@ static PyMethodDef usbobserver_methods[] = {
|
|||||||
{"get_mounted_filesystems", usbobserver_get_mounted_filesystems, METH_VARARGS,
|
{"get_mounted_filesystems", usbobserver_get_mounted_filesystems, METH_VARARGS,
|
||||||
"Get mapping of mounted filesystems. Mapping is from BSD name to mount point."
|
"Get mapping of mounted filesystems. Mapping is from BSD name to mount point."
|
||||||
},
|
},
|
||||||
{"send2trash", usbobserver_send2trash, METH_VARARGS,
|
|
||||||
"send2trash(unicode object) -> Send specified file/dir to trash"
|
|
||||||
},
|
|
||||||
{"user_locale", usbobserver_user_locale, METH_VARARGS,
|
{"user_locale", usbobserver_user_locale, METH_VARARGS,
|
||||||
"user_locale() -> The name of the current user's locale or None if an error occurred"
|
"user_locale() -> The name of the current user's locale or None if an error occurred"
|
||||||
},
|
},
|
||||||
|
@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.constants import islinux, isosx, get_osx_version, DEBUG, ispy3
|
from calibre.constants import islinux, isosx, get_osx_version, DEBUG, plugins
|
||||||
from polyglot.builtins import unicode_type
|
from polyglot.builtins import unicode_type
|
||||||
|
|
||||||
|
|
||||||
@ -132,32 +132,15 @@ class DummyNotifier(Notifier):
|
|||||||
class AppleNotifier(Notifier):
|
class AppleNotifier(Notifier):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ok = False
|
self.cocoa, err = plugins['cocoa']
|
||||||
import os, sys
|
self.ok = not err
|
||||||
try:
|
|
||||||
self.exe = os.path.join(sys.console_binaries_path.replace(
|
|
||||||
'console.app', 'calibre-notifier.app'), 'Calibre')
|
|
||||||
self.ok = os.access(self.exe, os.X_OK)
|
|
||||||
import subprocess
|
|
||||||
self.call = subprocess.Popen
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def notify(self, body, summary):
|
def notify(self, body, summary):
|
||||||
def encode(x):
|
|
||||||
if ispy3:
|
|
||||||
if isinstance(x, bytes):
|
|
||||||
x = x.decode('utf-8')
|
|
||||||
else:
|
|
||||||
if isinstance(x, unicode_type):
|
|
||||||
x = x.encode('utf-8')
|
|
||||||
return x
|
|
||||||
|
|
||||||
cmd = [self.exe, '-activate',
|
|
||||||
'net.kovidgoyal.calibre', '-message', encode(body)]
|
|
||||||
if summary:
|
if summary:
|
||||||
cmd += ['-title', encode(summary)]
|
title, informative_text = summary, body
|
||||||
self.call(cmd)
|
else:
|
||||||
|
title, informative_text = body, None
|
||||||
|
self.cocoa.send_notification(None, title, informative_text)
|
||||||
|
|
||||||
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
|
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
|
||||||
timeout, body, summary = self.get_msg_parms(timeout, body, summary)
|
timeout, body, summary = self.get_msg_parms(timeout, body, summary)
|
||||||
|
@ -7,6 +7,80 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
const char*
|
||||||
|
cocoa_send2trash(const char *utf8_path) {
|
||||||
|
NSString *path = [[NSString alloc] initWithUTF8String:utf8_path];
|
||||||
|
NSURL *url = [NSURL fileURLWithPath:path];
|
||||||
|
const char *ret = NULL;
|
||||||
|
NSError* ns_error = nil;
|
||||||
|
if (![[NSFileManager defaultManager] trashItemAtURL:url resultingItemURL:nil error:&ns_error]) {
|
||||||
|
ret = strdup([[ns_error localizedDescription] UTF8String]);
|
||||||
|
}
|
||||||
|
[url release];
|
||||||
|
[path release];
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern void macos_notification_callback(const char*);
|
||||||
|
|
||||||
|
@interface NotificationDelegate : NSObject <NSUserNotificationCenterDelegate>
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
cocoa_send_notification(const char *identifier, const char *title, const char *subtitle, const char *informativeText, const char* path_to_image) {
|
||||||
|
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
||||||
|
if (!center) {return;}
|
||||||
|
if (!center.delegate) center.delegate = [[NotificationDelegate alloc] init];
|
||||||
|
NSUserNotification *n = [NSUserNotification new];
|
||||||
|
NSImage *img = nil;
|
||||||
|
if (path_to_image) {
|
||||||
|
NSString *p = [NSString stringWithUTF8String:path_to_image];
|
||||||
|
NSURL *url = [NSURL fileURLWithPath:p];
|
||||||
|
img = [[NSImage alloc] initWithContentsOfURL:url];
|
||||||
|
[url release]; [p release];
|
||||||
|
if (img) {
|
||||||
|
[n setValue:img forKey:@"_identityImage"];
|
||||||
|
[n setValue:@(false) forKey:@"_identityImageHasBorder"];
|
||||||
|
}
|
||||||
|
[img release];
|
||||||
|
}
|
||||||
|
#define SET(x) { \
|
||||||
|
if (x) { \
|
||||||
|
NSString *t = [NSString stringWithUTF8String:x]; \
|
||||||
|
n.x = t; \
|
||||||
|
[t release]; \
|
||||||
|
}}
|
||||||
|
SET(title); SET(subtitle); SET(informativeText);
|
||||||
|
#undef SET
|
||||||
|
if (identifier) {
|
||||||
|
n.userInfo = @{@"user_id": [NSString stringWithUTF8String:identifier]};
|
||||||
|
}
|
||||||
|
[center deliverNotification:n];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation NotificationDelegate
|
||||||
|
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
|
||||||
|
didDeliverNotification:(NSUserNotification *)notification {
|
||||||
|
(void)(center); (void)(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) userNotificationCenter:(NSUserNotificationCenter *)center
|
||||||
|
shouldPresentNotification:(NSUserNotification *)notification {
|
||||||
|
(void)(center); (void)(notification);
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) userNotificationCenter:(NSUserNotificationCenter *)center
|
||||||
|
didActivateNotification:(NSUserNotification *)notification {
|
||||||
|
(void)(center);
|
||||||
|
macos_notification_callback(notification.userInfo[@"user_id"] ? [notification.userInfo[@"user_id"] UTF8String] : NULL);
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
double
|
double
|
||||||
|
@ -8,6 +8,10 @@
|
|||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
|
||||||
extern double cocoa_cursor_blink_time(void);
|
extern double cocoa_cursor_blink_time(void);
|
||||||
|
extern void cocoa_send_notification(const char *identitifer, const char *title, const char *subtitle, const char *informativeText, const char* path_to_image);
|
||||||
|
extern const char* cocoa_send2trash(const char *utf8_path);
|
||||||
|
|
||||||
|
static PyObject *notification_activated_callback = NULL;
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
cursor_blink_time(PyObject *self) {
|
cursor_blink_time(PyObject *self) {
|
||||||
@ -16,8 +20,54 @@ cursor_blink_time(PyObject *self) {
|
|||||||
return PyFloat_FromDouble(ans);
|
return PyFloat_FromDouble(ans);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
macos_notification_callback(const char* user_id) {
|
||||||
|
if (notification_activated_callback) {
|
||||||
|
PyObject *ret = PyObject_CallFunction(notification_activated_callback, "z", user_id);
|
||||||
|
if (ret == NULL) PyErr_Print();
|
||||||
|
else Py_DECREF(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
set_notification_activated_callback(PyObject *self, PyObject *callback) {
|
||||||
|
(void)self;
|
||||||
|
if (notification_activated_callback) Py_DECREF(notification_activated_callback);
|
||||||
|
notification_activated_callback = callback;
|
||||||
|
Py_INCREF(callback);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
send_notification(PyObject *self, PyObject *args) {
|
||||||
|
(void)self;
|
||||||
|
char *identifier = NULL, *title = NULL, *subtitle = NULL, *informativeText = NULL, *path_to_image = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "zsz|zz", &identifier, &title, &informativeText, &path_to_image, &subtitle)) return NULL;
|
||||||
|
cocoa_send_notification(identifier, title, subtitle, informativeText, path_to_image);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
send2trash(PyObject *self, PyObject *args) {
|
||||||
|
(void)self;
|
||||||
|
char *path = NULL;
|
||||||
|
if (!PyArg_ParseTuple(args, "s", &path)) return NULL;
|
||||||
|
const char *err = cocoa_send2trash(path);
|
||||||
|
if (err) {
|
||||||
|
PyErr_SetString(PyExc_OSError, err);
|
||||||
|
free((void*)err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
static PyMethodDef module_methods[] = {
|
static PyMethodDef module_methods[] = {
|
||||||
{"cursor_blink_time", (PyCFunction)cursor_blink_time, METH_NOARGS, ""},
|
{"cursor_blink_time", (PyCFunction)cursor_blink_time, METH_NOARGS, ""},
|
||||||
|
{"set_notification_activated_callback", (PyCFunction)set_notification_activated_callback, METH_O, ""},
|
||||||
|
{"send_notification", (PyCFunction)send_notification, METH_VARARGS, ""},
|
||||||
|
{"send2trash", (PyCFunction)send2trash, METH_VARARGS, ""},
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ if iswindows:
|
|||||||
return delegate_recycle(path)
|
return delegate_recycle(path)
|
||||||
|
|
||||||
elif isosx:
|
elif isosx:
|
||||||
u = plugins['usbobserver'][0]
|
u = plugins['cocoa'][0]
|
||||||
if hasattr(u, 'send2trash'):
|
if hasattr(u, 'send2trash'):
|
||||||
def osx_recycle(path):
|
def osx_recycle(path):
|
||||||
if isbytestring(path):
|
if isbytestring(path):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user