64-bit Omni Frameworks

I incorporated the OmniInspector Framework into my Mac OS X app back in the days when 10.5 was news, and valid architectures was ppc and i386.

I decided to update my app and its frameworks to x86_64. I downloaded the latest Omni Frameworks package from github, and compiled it. There were immediately some problems when I next tried to compile my app. Previously I could get a OIInspectorRegistry by calling +sharedInspector. This method no longer exists. The class method inspectorRegistryForMainWindow returns nil. My subclass defined with the key OFControllerClass in the info.plist is never instantiated. My app’s inspector menu, which previously was fully populated, is blank. No tabbed inspector appears when the app is run. The link between my app and the frameworks is totally broken.

Something has changed. Previously something triggered the instantiation of the necessary controllers and inspectors. This does not happen with the new code. I note from other Omni apps that the info.plist has an OFRegistrations key, which my app never needed before. Perhaps I need to set something under this key?

I’m thinking that this is not a hard problem. That there is just some simple thing that I need to fix in the configuration. But I have no idea what that might be.

My other option is to hack my old version of the frameworks to compile 64-bit code, but I suspect that may not be easy going.

My old version of the Omni Frameworks is close to the branch on github called MacOSX-10.5. Back in those days, the Omni inspectors belonged to the app, which had an Inspector menu, with an item of type OIDynamicInspectorMenuItem. Instantiating that item is a complicated task, which results in the instantiation of all the inspectors and their panels and panes. Since the inspectors belonged to the app, there was a single, shared registry of class OIInspectorRegistry, which the app could get by calling [OIInspectorRegistry sharedInspector].

This model has been replaced by a new one, where inspector registries belong to individual windows, and there is no shared registry. Instead, during initialization, the framework calls on the app delegate to return an inspector for the window. The app delegate must implement inspectorRegistryForWindow:. (Of course apps that keep to the old inspector model will ignore the window argument.) Then the inspector panel will appear along with the various inspector views.

The images for inspector tabs are, however, displayed badly. The Omni extension to NSImage, NSImage-OAExtensions.m, draws the image and then covers it with a solid tint using an operation that covers the non-transparent parts of the image. I’m pretty sure this is a bug, and I simply commented out the tint operation in the source.

The old shared registry responded to a OIInspectorSelectionDidChangeNotification notification by sending the newly selected inspector a inspectObjects message, which gave the inspector the opportunity to update its view.

This notification no longer exists. The app instead calls [OIInspectorRegistry updateInspectorForWindow:nil]. The registry responds by sending addInspectedObjects to the responder chain. Somewhere in the responder chain, a class must adopt the OIInspectableController protocol, and implement addInspectedObjects, where the class adds the objects it wants to be inspected to the inspection set. After this, the current inspectors get the inspectObjects message.

Now inspector views appear and can be updated based on what is selected in the document. Things are looking good. But…

The Inspector menu doesn’t work; none of the items are enabled. The first step of the fix for this is to make the app a subclass of OAApplication. This causes validateMenuItem to be called when the user clicks on the Inspector menu. This would be great, except for some weirdness in the implementation of that method.

First of all, all inspector menu items, regardless of their class, are created with the action revealEmbeddedInspectorFromMenuItem. This is not a good idea, because only OITabbedInspectors respond to this message.
However, the situation is even worse. OIInspectorRegistry’s validateMenuItem contains an assertion that the inspector is an instance of OITabbedInspector. If the inspector is a plain, old OIInspector, then validateMenuItem crashes your app. All OIInspectors have to go.

If you want to have more than one inspector panel, they must all be tabbed inspectors. However, it gets worse. The code for validateMenuItems allows for the possibility that there is more than one controller. It loops through the controllers, and asked each one (a tabbed inspector) to return the sub-inspector with the identifier assigned to the menu item. But the code does not check whether the first inspector it asks returns nil; it accepts this, possibly nil, value, asks it if it is visible, sets the check mark on the menu item accordingly, and then return YES. It is impossible for the loop to iterate to a second controller. This means that not only can you not have an OIInspector, you can have only one tabbed inspector.

The implementation of revealEmbeddedInspectorFromMenuItem in OIInspectorRegistry.m has the same problems. All controllers are required to be tabbed inspectors, and none of the controllers except the first one will be queried for a sub-inspector matching the menu item’s identifier.

As far as I have been able to determine, if you have old style inspectors, and take all these matters into account, you will be able to adapt to the new OmniInspector framework. There may be some further issues I will encounter, or there may be some other issues I forgot to mention. In either case, I will update this post.