how can mori plugins learn about changes from entries?

Tags ( )

Yes, I would very much like to extend from an entry delegate (rather than register for notifications) but if my plugin becomes a delegate of the entries, won't that prevent other plugins from doing the same?

This was the case in Mori 1.1, but I changed the design and now entry delegates are no longer associated with a single entry kind. Instead all entry delegates get notified of all changes. In some cases where the entry delegate is responsible for implementing some behavior (such as giving an entry an icon) the first valid response is used. So in the case of icons the icon will be used from the first entry delegate that doesn't return a nil icon. You can see this in action by looking at the classes MIEntryDelegate and MIEntryDataRTFDDelegate.

MIEntryDelegate is a generic delegate, and enforces constrains that are the same for all entries. And when asked for something that applies to a specific entry (such as it's icon, or if it can be copied) it just returns a default value... allowing some other entry delegate to respond.

While the MIEntryDataRTFDDelegate is what gives file entries their unique behavior (icon mostly), but also validates that a file entries can only have other file entries as children.

Could I pepper you with a

Could I pepper you with a few more questions, so that I can understand
how the delegates are working (and hopefully avoid bugging you later
with problems.) :) Your answers can be as short as you like, and I
should be able to get it.

1) why do all entries register a change on the open of a notebook?

I'm not sure to tell the truth. The didChange methods that the delegates get are just forwarded from the MIEntry and MIEntryData didChangeValueForKey methods. And those are called by core data. My guess is that core data is calling them as it fills in the values that it caches from the backing database. So even though from the entire system perspective the value isn't changing, from the managed object's perspective the cache is being filled, going from nil to the value from the database. Anyway those delegate methods should be called anytime the didChangeValueForKey: method on NSManagedObject is called.

2) do all entry delegate extensions (plugins extending a delegate
point) get called on app open?

No. Plugins are loaded by demand. So if your plugin extends from a point that is loaded when the application starts (such as com.hogbaysoftware.BKLifecycle) then your plugin will be loaded when the application starts. But if your plugin loads from a point that isn't loaded at startup (such as com.hogbaysoftware.mori.MIDocument.entryImport) then the plugin won't load until needed. So it really depends on the extension point.

when I edit one (already existing) column value (lead) with my
EntryDelegateExtension plugin installed, I get this console output:

2006-04-14 15:54:56.996 Mori[3428] received change value for entry data
2006-04-14 15:54:56.996 Mori[3428] lead
2006-04-14 15:54:56.997 Mori[3428] recognized key as lead
2006-04-14 15:54:56.997 Mori[3428] entered updateOnNewLead
2006-04-14 15:54:56.997 Mori[3428] will go to didChange from didSet
2006-04-14 15:54:56.997 Mori[3428] received change value for entry data
2006-04-14 15:54:56.998 Mori[3428] lead
2006-04-14 15:54:56.998 Mori[3428] recognized key as lead
2006-04-14 15:54:56.998 Mori[3428] entered updateOnNewLead
2006-04-14 15:54:56.998 Mori[3428] received change value for entry data
2006-04-14 15:54:56.999 Mori[3428] entryDataID

3) It seems that I get a call for the didChangeValueForEntryDataKey as
well as the didSetValueForEntryDataKey. Since, I am currently
forwarding didSet to didChange, it looks like I only need to look for
and process one of them. How are they different?

You should always get didChangeValueForEntryDataKey called for any change. But you can also get it when you wouldn't expect it, such as when a notebook first opens. You should only get didSet called when the user goes and changes a value directly. In particular didSet should not be called when the notebook loads, and also shouldn't be called as a result of the user executing the undo command. In general you should only need to put your logic in one of these methods, if you want everything use the didChange, if you just want to know when the user sets a value use that one.

When in doubt didChange is probably the one what you want.

4) Why does a "entryDataID" change register? this seems to happen for
any entryData changes.

This shouldn't be necessary, but it's a workaround for a bug in Coredata. See:

http://www.cocoabuilder.com/archive/message/cocoa/2006/3/15/158768

The problem seems to be that when coredata has a predicate running on a keyValuePath such as entry.entryData or entry.entryData.note then the search isn't property updated unless an attribute value changes in all objects along that path. So anytime a value changes in the entryData I also say that a attribute value (i picked entryID, but any attribute value would have worked) has changed in the entry. But I don't actually change the value I just fire willChange, didChange calls. Do a search in Mori's code for dirtyHackSoFetchWorksOnManagedObjectContextWithoutSaving to see where this is happening. Its a similar story when an entryData's not changes, in that case I called willChange/didChange on the entryData's entryDataID attribute. But even so I am probably firing those changes more then is necessary. I think I really only need to fire the entryDataID change when the note changes, and not when any attribute in the entryData changes. I'll look into removing some of those calls in the next release. Well it's not a very clear subject, and my description was even less clear, but I hope it gives you a bit of an idea of what is happening behind the scenes.

2) do all entry delegate

2) do all entry delegate extensions (plugins extending a delegate
point) get called on app open?
No. Plugins are loaded by demand. So if your plugin extends from a point that is loaded when the application starts (such as com.hogbaysoftware.BKLifecycle) then your plugin will be loaded when the application starts. But if your plugin loads from a point that isn't loaded at startup (such as com.hogbaysoftware.mori.MIDocument.entryImport) then the plugin won't load until needed. So it really depends on the extension point.

For this one, I was really just wondering about the new delegate extensions, specifically, an entryDelegate. It automatically gets loaded on mori startup, correct? (I guess and importDelegate would wait until you tried to import something.)
Is there anything that would cause more than one -init messages to get sent to an entryDelegateExtension. I sometimes find that the extension class object gets 2-4 -init messages on mori startup, and I haven't yet been able to figure out why?

(I'm not sure, but it seems that this just started happening once I started submitting core data queries like you outlined in your other thread. Although it may be how I itterate over the results.)

Comment viewing options

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