Calendar Store: how to distinguish event calendar from task calendar? - macos

I'm working on an app that has a reminder component. I'm using Calendar Store to get a list of calendars, and I want the user to choose which calendar they'd like to add a task to. The problem is, CalCalendar seems not to distinguish between event calendars and task calendars.
NSArray* calendars = [[CalCalendarStore defaultCalendarStore] calendars];
for( CalCalendar* aCalendar in calendars ) {
if( aCalendar.isEditable ) {
NSLog( #"editable calendar: %#", aCalendar );
}
}
This outputs:
editable calendar: CalCalendar <0x6e04d10> {UID = 8AA8FFAD-D781-47F7-9231-CF66E1753983; title = Work; notes = (null); color = NSCalibratedRGBColorSpace 0.054902 0.380392 0.72549 1; type = CalDAV; editable = 1}
editable calendar: CalCalendar <0x6e05000> {UID = A7F4A1B2-D1CF-4A20-9F84-CD1A1E99773E; title = Home; notes = ; color = NSCalibratedRGBColorSpace 0.72549 0.054902 0.156863 1; type = CalDAV; editable = 1}
editable calendar: CalCalendar <0x6e050f0> {UID = 43B14D2A-9976-461C-8EFE-5FA029381828; title = Personal; notes = (null); color = NSCalibratedRGBColorSpace 0.901961 0.784314 0 1; type = CalDAV; editable = 1}
editable calendar: CalCalendar <0x6e05140> {UID = F42EC365-20AC-4251-B45E-FB7F169928F0; title = Mac; notes = (null); color = NSCalibratedRGBColorSpace 0.054902 0.380392 0.72549 1; type = Local; editable = 1}
editable calendar: CalCalendar <0x6e05190> {UID = FF771FF9-3969-4001-BBA4-9B7B00E80291; title = Cloud 2; notes = (null); color = NSCalibratedRGBColorSpace 0.054902 0.380392 0.72549 1; type = CalDAV; editable = 1}
editable calendar: CalCalendar <0x6e051e0> {UID = 40234537-869C-4CC2-89B9-DD4F7D36C169; title = Groceries; notes = ; color = NSCalibratedRGBColorSpace 0.443137 0.101961 0.462745 1; type = CalDAV; editable = 1}
I know that the first 2 are event calendars, and the last 4 are task lists. And, iCal definitely knows the difference, because it shows only event calendars for events, and task calendars for tasks.
But there appears to be no way via the Calendar Store APIs to determine this programmatically, unless I'm missing something.
Update: I see I'm not the only one to notice this, as I found rdar://10377730. I've just filed my own report, as rdar://10980542

I'm not super happy with it, but the workaround I'm using now is to simply try to create a task in each calendar. If you try to create a task in an event calendar, you'll get an error. It looks a little something like:
- (BOOL) isCalendarAUsableTaskList:(CalCalendar*)aCalendar
{
if( !aCalendar.isEditable ) return NO;
// Try to make a task here.
CalTask* newTask = [CalTask task];
newTask.calendar = aCalendar;
newTask.title = #"Test Item";
NSError* anError = nil;
if( ![[CalCalendarStore defaultCalendarStore] saveTask:newTask error:&anError] ) {
// Couldn't make a task, this calendar is no bueno.
NSLog( #"Error saving task to calendar %# (%#)", aCalendar.title, [anError localizedDescription] );
return NO;
}
// Created a task. Now clean up on our way out.
NSLog( #"Saved task to calendar %#", aCalendar.title );
[[CalCalendarStore defaultCalendarStore] removeTask:newTask error:nil];
return YES;
}

Related

Getting 'Error: AppleEvent handler failed' every time I run this script in Script Editor

Been struggling to get this script to work. It's meant to batch export notes out of Apple Notes. Script is below.
// set things up
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var notesApp = Application('Notes');
notesApp.includeStandardAdditions = true;
// choose which notes
var notes = notesApp.notes;
var whichNotes = app.chooseFromList(notes.name(), { withPrompt: "Which Notes?", multipleSelectionsAllowed: true });
if (whichNotes) {
// choose save location
var saveWhere = app.chooseFolder().toString();
if (saveWhere) {
// loop through all notes
for(var i=0; i<notes.length; i++) {
// is this note one to be exported?
if (whichNotes.indexOf(notes[i].name()) > -1) {
// save file as html
var filename = saveWhere+"/"+notes[i].name()+".html";
var file = app.openForAccess(Path(filename), { writePermission: true });
app.setEof(file, { to: 0 });
app.write(notes[i].body(), {to: file});
app.closeAccess(file);
}
}
}
}
A bunch of other people have used it with no problems.
I have the same problem with the same script on 10.15.7. The issue is raised on notes.name().
I assume this is related to either too many notes (it used to work, but I created a lot of notes since), or some special char in the note title. But I did not managed to fix it with my notes.
I copied my version below.
(notice the replace to build a valid file name if your note title contain "/".)
// set things up
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var notesApp = Application('Notes');
notesApp.includeStandardAdditions = true;
// choose which notes
var notes = notesApp.notes;
this.console.log("before notes.name()")
var whichNotes = app.chooseFromList(notes.name(), { withPrompt: "Which Notes?", multipleSelectionsAllowed: true });
this.console.log("After notes.name()")
this.console.log("Let's do it") // view / show log / message tab
if (whichNotes) {
// choose save location
var saveWhere = app.chooseFolder().toString();
if (saveWhere) {
this.console.log("note count:"+notes.length)
// loop through all notes
for(var i=0; i<notes.length; i++) {
// is this note one to be exported?
if (whichNotes.indexOf(notes[i].name()) > -1) {
// save file as html
var notename = notes[i].name().replace(/\//gi,'-')
this.console.log("next:"+notename) // view / show log / message tab
var filename = saveWhere+"/"+ notename +".html";
var file = app.openForAccess(Path(filename), { writePermission: true });
app.setEof(file, { to: 0 });
app.write(notes[i].body(), {to: file});
app.closeAccess(file);
}
}
}
}

Programmatically change screen saver settings

I'm trying to programmatically change the configured screen saver.
If I do
$ defaults -currentHost read com.apple.screensaver
{
CleanExit = YES;
PrefsVersion = 100;
idleTime = 600;
moduleDict = {
moduleName = XXXX;
path = "/Users/juanjo/Library/Screen Savers/XXXX.saver";
type = 0;
};
showClock = 0;
}
I can see that info or even update it using the write parameter.
This settings are not accesibles via NSUserDefaults but they are via CF.
I was able to do this by code
var moduleDict = CFPreferencesCopyAppValue("moduleDict", "com.apple.screensaver") as CFDictionary
but when I try to get one value from that "dict" the program crash in this line:
var saverName = CFDictionaryGetValue(moduleDict, "moduleName")
saying EXC_BAD_ACCESS (code=EXC_I386_GPFLT).
So, do you know how can I read this values and write back?
As I mentioned in Cocoa-Dev list, I was able to do it this way.
Reading:
var moduleDict = CFPreferencesCopyAppValue("moduleDict", "com.apple.screensaver") as NSDictionary
var saverName = moduleDict["moduleName"] as String!
Writing:
var moduleDict = CFPreferencesCopyAppValue("moduleDict", "com.apple.screensaver") as NSDictionary
var mutable = moduleDict.mutableCopy() as NSMutableDictionary
mutable["moduleName"] = "MyScreenSaver"
mutable["path"] = mySaverPath
CFPreferencesSetValue("moduleDict", mutable as CFPropertyList, "com.apple.screensaver", kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
CFPreferencesAppSynchronize("com.apple.screensaver")

Test for Firefox being hidden in OS X

Is there a way I test to see if Firefox windows are hidden in OS X via the "Hide Firefox" (Command-H) option? Enumerating using nsIWindowMediator says that the hidden attribute is false and the windowState is STATE_NORMAL for these hidden windows in OS X. Thanks!
Try console.log(window) or if you dont have access to thew indow object Services.appShell.hiddenDOMWindow.console.log(Services.wm.getMostRecentWindow('navigator:browser')) then in browser console go and click '[object ChromeWindow]' and then it will print all its enumerable and non-enumerable properties.
Maybe it will show something in here differently when it's hidden I'm not sure.
i dont know but heres a way that might help you figure out.
install this addon from github
and then in scratchpad in browser environment run Cu.import('chrome://cdumpjsm/content/cDump.jsm'); cDump(window,{depth:0}) when hidden, then save that file. then show the window then run cDump again, then diff the files
Ok at long last here is your solution. js-ctypes using objective-c:
Tested on OS 10.9
GitHubGIST :: Noitidart / _ff-addon-snippet-OSXHidden.js
Cu.import('resource://gre/modules/ctypes.jsm');
var objc = ctypes.open(ctypes.libraryName('objc'));
// types
var id = ctypes.voidptr_t;
var SEL = ctypes.voidptr_t;
var BOOL = ctypes.signed_char;
// constants
var nil = ctypes.voidptr_t(0);
//common functions
var objc_getClass = objc.declare('objc_getClass', ctypes.default_abi, id, ctypes.char.ptr);
var sel_registerName = objc.declare('sel_registerName', ctypes.default_abi, SEL, ctypes.char.ptr);
var objc_msgSend = objc.declare('objc_msgSend', ctypes.default_abi, id, id, SEL, '...');
//common selectors
var alloc = sel_registerName('alloc');
var init = sel_registerName('init');
var release = sel_registerName('release');
//now using docs here: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSApplication_Class/index.html#//apple_ref/occ/instp/NSApplication/hidden
// pool = [[NSAutoreleasePool alloc] init]
var NSAutoreleasePool = objc_getClass('NSAutoreleasePool');
var pool = objc_msgSend(objc_msgSend(NSAutoreleasePool, alloc), init);
// NSApp = [NSApplication sharedApplication];
var NSApplication = objc_getClass('NSApplication');
var sharedApplication = sel_registerName('sharedApplication');
var NSApp = objc_msgSend(NSApplication, sharedApplication);
// [NSApp isHidden]
var isHidden = sel_registerName('isHidden');
var objc_msgSend_returnBool = objc.declare('objc_msgSend', ctypes.default_abi, BOOL, id, SEL, '...'); //this is return value because `isHidden` returns a BOOL per the docs
var rez_isHidden = objc_msgSend_returnBool(NSApp, isHidden);
console.info('rez_isHidden:', rez_isHidden, rez_isHidden.toString(), uneval(rez_isHidden));
if (rez_isHidden == 0) {
console.log('Firefox is HIDDEN!');
} else if (rez_isHidden == 1) {
console.log('Firefox is showing.');
} else {
console.warn('rez_isHidden was not 0 or 1, this should never happen, if it did, objc should error and crash the browser');
}
// [pool release]
objc_msgSend(pool, release);
objc.close();
Credit to #arai for teaching me objc jsctypes! When no one else would the guy spoon fed me!!! More people should spoon feed each other!

Change tile navigation after tile update on Windows Phone

I'm sending a tile update to my app users. After the tile update I would like to automatically direct the user to the new updated page once he clicks the new tile. Is there any way I can achieve this?
I can't pull the server anymore because it's a fire & forget mechanism.
With toast notifications I can use the following message in my notification:
<wp:Param>/Views/MyPage.xaml</wp:Param>
But that doesn't seem to work with my FlipTile update.
Im not quite sure if this is what you are looking for, but can you just replace the Tile once the app is loaded? Somewhat like this:
/// <summary>
/// Creates or replaces a tile with a Title and a parameter
/// </summary>
public void replaceTile()
{
//ShellTile TileToFind = ShellTile.ActiveTiles.First();
string tileTitle = "Title of the Tile";
//You might want to use your Parameter from the toast here as the PageName
ShellTile TileToFind = FindTile( "/PageName.xaml?parameter" );
StandardTileData NewTileData = new StandardTileData
{
Title = tileTitle,
BackgroundImage = new Uri( "/Resources/tile_icon.png", UriKind.Relative ),
Count = 0,
BackTitle = "",
//BackBackgroundImage = new Uri("/Resources/appicon_large.png", UriKind.Relative),
BackBackgroundImage = new Uri( "", UriKind.Relative ),
BackContent = ""
};
// Application should always be found, since it is always the first tile (even if not on homescreen)
if ( TileToFind == null )
{
// Update the Application Tile with the NewTileData
//TileToFind.Update( NewTileData );
//Or replace an existing (second) tile with the new, updated Tile
//ShellTile alreadyExistingTile = FindTile( "/PageName.xaml?parameter" );
//if ( alreadyExistingTile != null )
//{
// alreadyExistingTile.Delete();
//}
ShellTile.Create( new Uri( "/PageName.xaml?parameter=" + newParameter, UriKind.Relative ), NewTileData );
}
else
{
TileToFind.Update( NewTileData );
}}

WP7 Popup not showing

In my app I want to display a simple string within a popup when the user clicks on an image. For this I added a Tap gesture listener to the image and within the handler I have the following code:
private void GestureListener_Tap( object sender, GestureEventArgs e )
{
var img = sender as Image;
if( img == null ) {
return;
}
Point pos = e.GetPosition( img );
string text = "I'm a popup!";
var popup = new Popup() {
Child = new Border() {
BorderBrush = new SolidColorBrush( Colors.LightGray ),
Child = new TextBlock() {
Text = text,
TextWrapping = TextWrapping.Wrap,
},
},
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalOffset = pos.X,
VerticalOffset = pos.Y,
Visibility = Visibility.Visible,
};
popup.IsOpen = true;
Debug.WriteLine( "GestureListener_Tap: " + text );
}
The call to WriteLine prints in the debugger output window but the popup doesn't get displayed. What am I doing wrong here?
Thanks for your help!
I tried your code and the Popup is displayed. I think the problem for you is the Position for the Image relative to the Mouse. Try to set another Background for the Parent Container and I think you'll see the Popup. You can also try to play around with
Point pos = e.GetPosition(null);
until you get the Position you require

Resources