Sending New entries to Mori

Tags ( )

HI,

I'm re-working Note-chan as a Cocoa app (mostly just for the practice with Cocoa) and I want this version to be able to include all of the clipboard contents in the note that Mori is able to handle. Anyone have any suggestions about what might be the easiest way to go about this? Unless I make note-chan into a plugin, doesn't it have to go through applescript to get a new entry made (I've been able to use NSApplescript successfully to get it done.)

So my ideas are:

1. format the clipboard and note text in note-chan as a NSAttributedString (can that handle attachments/images?). Then send it through Applescript to Mori.

2. Send the note text first, then try to get Mori to Paste the clipboard contents into the note.

3. Somehow use the Mori clipping services to do the work.

Any hints about how to proceed on the most likely successful of these, would be greatly appreciated.

Thanks

It's probably possible with

It's probably possible with applescript (by scripting the GUI to past into the right place), but it looks to me like using NSPerformService(@"Mori/Make New Entry", pboard) is the easiest method. After invoking the service you might use applescript to move the entry to some folder specified in Note-chan's window. Also it looks like perform service should be a common mechanism that you can use to send info to other apps. I did read that one problem with using NSPerformService is that it will break if the service menu item name changes, but it seems like ease of use should probably win out in this case.

Thanks, I'll look into the

Thanks, I'll look into the NSPerformService.
If I was going to try to format the entire note internally in Note-chan (both clipboard contents and user input note, and text attributes) which cocoa type (NSWhatever) could I then use with the applescript "make entry with properties {note: }"?

I think (at least it does in

I think (at least it does in Mori and TextEdit) that the "text" type defined in the "Text Suite" corresponds to a NSTextStorage object... or at least there is a mapping between the two. I don't think that applescript really knows anything about NSTextStorage.

But as you've probably seen spread throughout the site, I've never really figured out how to satisfactorily access the full capability of the NSTextStorage from the applescript side. You might try finding some applescript experts and just describe your problem in terms of TextEdit. Since if you can do what you need scripting text edit the same thing should work in Mori.

I asked the AS guru's at

I asked the AS guru's at macscripter.net, and it seems that applescript doesn't pass rich text very easily.

Here's the thread for anyone who's curious.
http://bbs.applescript.net/viewtopic.php?pid=52725#p52725

pboard appending

I'm looking into using the Mori service to send the note text (from note-chan) and clipboard contents over to Mori, but I'm having a little trouble finding how to combine the note and the clipboard into one single pasteboard.
Any hints to get me started on adding the string of the note window to the contents of the clipboard?

Thanks

I think you need to read the

I think you need to read the text from the pboard, append your text, and then write the combined text back to the pboard. Here's some code to read the basic text types from the pasteboard:

if ([[pboard types] containsObject:NSRTFDPboardType]) {
return [[[NSMutableAttributedString alloc] initWithRTFD:[pboard dataForType:NSRTFDPboardType] documentAttributes:nil] autorelease];
} else if ([[pboard types] containsObject:NSRTFPboardType]) {
return [[[NSMutableAttributedString alloc] initWithRTF:[pboard dataForType:NSRTFPboardType] documentAttributes:nil] autorelease];
} else if ([[pboard types] containsObject:NSStringPboardType]) {
return [[[NSMutableAttributedString alloc] initWithString:[pboard stringForType:NSStringPboardType]] autorelease];
}

NSPerformService works well

NSPerformService works well when I send it the generalPasteboard direction (i.e. without alteration) and when I send it a private pasteboard from a NSString (taken from a text field). However, when I try to read the info from the clipboard and append a string, I get nothing.


- (IBAction)sendNote:(id)sender{
NSString *noteString = [self formatEntryString];
NSLog(noteString);
NSString *scriptString =[[NSString alloc] initWithString:[@"test \"" stringByAppendingString:noteString]];
NSLog(scriptString);
NSPasteboard *tempPB = [NSPasteboard pasteboardWithUniqueName];
NSAttributedString *formattedNote = [[NSAttributedString alloc] initWithString:scriptString];
NSLog([formattedNote string]);
NSPasteboard *genPB = [NSPasteboard generalPasteboard];
NSMutableAttributedString *clipboardString = [self getPBdata:genPB];
NSLog(@"returned from getPBdata");
NSLog([clipboardString string]);
if (clipboardString){[clipboardString insertAttributedString:formattedNote atIndex:[clipboardString length]];}
NSLog([clipboardString string]);
NSData *rtfData = [clipboardString RTFDFromRange:NSMakeRange(0, [clipboardString length])
documentAttributes:nil];
[tempPB declareTypes:[NSArray arrayWithObject:NSRTFDPboardType] owner:self];
[tempPB setData:rtfData forType:NSRTFDPboardType];
NSPerformService(@"Mori/Make New Entry", tempPB);
NSPerformService(@"Mori/Make New Entry Without Styles", tempPB);
[tempPB release];
}

- (NSMutableAttributedString *)getPBdata:(NSPasteboard *)pboard{
NSLog(@"entered getPBdata");
if ([[pboard types] containsObject:NSRTFDPboardType]) {
return [[[NSMutableAttributedString alloc] initWithRTFD:[pboard dataForType:NSRTFDPboardType] documentAttributes:nil] autorelease];
} else if ([[pboard types] containsObject:NSRTFPboardType]) {
return [[[NSMutableAttributedString alloc] initWithRTF:[pboard dataForType:NSRTFPboardType] documentAttributes:nil] autorelease];
} else if ([[pboard types] containsObject:NSStringPboardType]) {
return [[[NSMutableAttributedString alloc] initWithString:[pboard stringForType:NSStringPboardType]] autorelease];
}
else return nil;
}

with a run log:
2006-02-18 13:26:03.411 Note-chanCocoa[12753] display dialog "note test
2006-02-18 13:26:03.432 Note-chanCocoa[12753] display dialog "note test
2006-02-18 13:26:03.432 Note-chanCocoa[12753] entered getPBdata
2006-02-18 13:26:03.454 Note-chanCocoa[12753] returned from getPBdata
2006-02-18 13:26:03.454 Note-chanCocoa[12753] random clipping
2006-02-18 13:26:03.454 Note-chanCocoa[12753] random clippingdisplay dialog "note test
2006-02-18 13:26:03.571 Note-chanCocoa[12753] Service "(null)" could not be provided: Couldn't read selection from pasteboard.

I'm pretty sure that it's somehow when I'm setting the data to the tempPB.
Any ideas?

Thanks

Distributed Objects

I was playing around this weekend with distributed objects, thinking it might do the trick here. The idea being a plugin in Mori could vend a "node" controller type object. The client would then have a proxy to the vended node controller and be able to create new nodes remotely.

Just as a proof-of-concept I created a test client and server, and got it working but the underlying network connections are hard to get really right. My client was basically a NSTextView with a send button. The server was a NSTextView with a start/stop server toggle button. When you press the send button the text in the NSTextView on the client was sent to the NSTextView on the server. It was working, text was being sent over from the client to the server once it was up, and you could bring down the server but you could never bring it back up again. It would just error out every time after that.

I've got a lot more playing to do. Searching around on the net, I couldn't find anyone who got this working properly. May have to stick with the server being brought up at init and being stopped at cleanup and that's it. Or we find another way to do this. Bonjour might be an option as well.

I've got more work to do on the error handling that the client side is doing as well. For example, if the server was down you'd get the spinning beach ball for a heck of a long time. I need to adjust the timeouts. I'll post the example code when I've got it a bit more presentable. But if anyone has any pointers or thoughts on bringing up and down NSConnection objects and the whole vended object/proxy infrustructure I'm all ears.

Phil

You could try making the

You could try making the server at the document level, that way you won't need to shut it down when a document closes. I think access to Mori notebooks from outside the mori process is important and will open up all kinds of fun possibilities. Note that you can do a lot of these things with applescript right now, but when you run into a problem with applescript (as discussed above) it's hard to figure the best way to get around the problem. Also I think applescript does work over networks, but probably distributed objects is a better way to do this.

Don't Ever Shut Down

That seems to be what everyone does ... they open the connection and vend the object at init time, and they shut it down at dealloc time. Don't even think of cycling it. But right now the code is entirely outside the context of Mori, it's just a chunk of demo code to play around with and get all the kinks out. I want to be able to toggle the server on and off so I can trigger all the subsequent connection errors to see if I am handling them properly. I cleaned up the client code last night so it is catching timeout exceptions and setting the send/receive timeouts to more normall levels (the default is huge). I also registered for a ConnectionDidDie notification on the client side, but I'm pretty sure that's useless. I haven't been able to trigger that. I think only the server kicks off that notification, and since it's in a completely different process the client never sees it. I need to make another pass through the server code and then I'll post what I've learned so far.

I'm also just doing NSStrings right now ... I want to add in rtf and rtfd examples. That was sort of the whole point.

Ultimately it would be very cool to hook the Mori plugin into Bonjour so a client would have a list of all the Mori notebooks on the subnet (that are being shared) and could add a note to any of them. But I'm pretty far from that point (if I ever manage to get there).

Phil

Regarding sending entries to

Regarding sending entries to Mori with notechan, I'd still like to utilize the NSService for a final independent version, but for now I've got the plugin version working 80%. I'm modeling the code for creating a new note on the readEntryCopiesFromPasteboard: method but I had a few questions.

1. Is the service working for all data types? I was testing it (just using the service menu items by hand, not my own code) from Safari to TextEdit to Mori, and styled text will transfer to TextEdit (through its service) no problem, and then to Mori nicely. But if I've selected images and Text in Safari, they'll make it to TextEdit but not into Mori (from TextEdit.)
2006-02-24 23:09:02.563 TextEdit[16703] Service "Make New Entry" could not be provided: Couldn't read selection from pasteboard.

2. MIEntry class is private, so we should use id with MIEntryProtocol when we create an entry?

3. I get an error:
2006-02-25 00:05:53.470 Mori[17067] *** -[MIEntryData setNoteTextStorageContent:]: selector not recognized [self = 0x1387860]
I'm pretty sure that I'm sending it an NSAttributedString, and I imported MIDocumentProtocols. Any ideas?

Thanks
Jeff

Ok: #3 is because I was

Ok:

#3 is because I was looking at at v40 which has the selector setNoteTextStorageContent and I'm running v39 which uses setTextStorageContent

#2 seems to be the right way

#1 I'm still wondering about

Thanks,
Jeff

Unfortunately I'm not sure

Unfortunately I'm not sure about #1 at the moment, but if it behaves as you says it's likely a bug in Mori. I think we should be accepting all service types that TextEdit does. Please add a bug report for this.

#2, yes plugins can't and shouldn't really know about MIEntry. Entries should be typed like this in plugin code.

id <MIEntryProtocol> myEntry;

And if you ever need to create a new entry you should create it using the API found in the MIDocumentProtocols.h file. Something like this:

id <MIEntryProtocol> myEntry = [NSObject insertNewEntryInDocument:document];

In particular you shouldn't be inserting entries using CoreData's NSEntityDescription class. Plugins shouldn't really know or care that Mori is built on CoreData.

Thanks for the tips. I got

Thanks for the tips. I got things working pretty well now.

Don't forget to remind people that after "insertNewEntryInDocument" you have to "addOrderedChild" to another entry somewhere to get the entry actually in the tree somehwhere and visible. Took a view minutes to find it.

Thanks

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.