Sunday, January 17, 2010

Moving MFC icons to Cocoa

Working my way through the timeline MFC to Cocoa conversion I have once again bumped up against graphic resources. Various components in the UI have graphic resources (icons). Back when I was getting the basic engine to work I compiled out all things of this ilk with a huge amount of other things with a

#define _NO_UI.

A part of my approach is that the structure of the new UI follows the PC version very closely so I am bumping into the way that Icons are handled within Cello as a whole. Basically this I am gradually working my way through some of these #if _NO_UI blocks.

Existing graphic resources in the PC version are stored as .bmp files. Each file typically has a number of icons. The various components that comprise the system (such as the 20-30 effects) have factory functions that specify a name for the component, and a bitmap resource with an offset as an icon. This is achieved by all the components inheriting from a base class that maintains this. The upshot is that I have to tackle this base class and bring it into line, and because I hit the constructor, I also have to attend to the many sub-classes that use icons.

In the OSX version of Cello I use NSImage to represent an icon - and as an alternative to specifying an icon resource and offset simply specify a name. This name is the name is nothing more than the filename that of the icon that appears in the bundle. I have written a really simple wrapper based on boost intrusive_ptr that wraps the NSImage for use in C++. The existing icons are appropriate to Windows and will look really out of place in OSX but that is a problem for another day and a problem that I hope to hand over to an icon designer. For place-holder icons I take the existing icons bring them to my old Power-PC and use an old copy of Photoshop to turn them into individual PNG files.

Monday, January 11, 2010

Mutating MFC to Cocoa

I am making progress converting the view hierarchy of the timeline to cocoa. In the about face I am working through the classes and interfaces that are defined in MFC and attempting a mechanical conversion where possible. In my first cut I do the following:
  • Mutate the all 'interface' definitions to protocols.
  • Mutate all pure virtual base classes into protocols.
  • Mutate all other classes as objective C classes.
  • Where a class is based on multiple inheritance of a C++ class (typically mix-in classes) this is expressed as a protocol and I write a subclass of the mix-in class that funnels calls to an object of that protocol.
  • Comment out what is obviously not needed for a first cut.
I do the conversion bit by bit getting the compiler to guide me. It is a relentless, tireless guide. At best I get logic and code that is very close to what I need at worst I get a fill-in-the-dots template. Importantly the code dovetails in nicely into the existing code.

Sunday, January 3, 2010

C++ Observers in Objective C

Cello has various notifications that are posted to observers. This is a good and fine thing - the observers are generally UI components and in the PC version of Cello are C++ classes. The form is that through multiple inheritance of a mix-in class a class becomes an observer. It overrides the observation methods and handles the notifications. The rub comes with Objective-C.

Under OSX all the UI components are Objective C classes these can not inherit (neither multiply nor singly) from a C++ class - yet they need to be observers. So how to solve the problem? There seem to be two possibilities:
  1. Get the Objective C classes to "somehow" observe.
  2. Reroute all messages through the NSNotification system.
I have chosen the first option. Why? Well I want to use as much of the original code as possible and I don't want to have to serialize all the information that is passed to the observer as Objective C objects.

So how to do it? The method I chose is fairly simple. It goes like this:
  • For each observer write an equivalent protocol to the virtual method(s) in the C++ mix-in class.
  • Write a C++ class that is a subclass of the C++ mix-in observer and forwards the observations to an Objective C object that conforms to the protocol.
  • Now write your Objective-C class. It should conform to the protocol. It also has a member that is a pointer to the forward class that it owns (creates and destroys) and forwards the messages.
It is a modest fiddle - but works well.

Saturday, January 2, 2010

TimeLine - an about-face

Sometimes programming is about heading down a blind alley. I guess that thing to do in these situations is to know when to call it a day and head back. So this is what I am doing with my current work on the timeline.

So why the about face? What I was doing with the Timeline was to develop a slot in replacement for something a bit like a NSTableView but with all the capabilities rewired for a TimeLine. My idea was to provide a protocol that would be expressive enough to allow a fairly simple mesh between the Objective C and C++ in a controller class. Thus the controller class would be a thin shim or adaptor between the two things. This idea is workable - but the question is "how much work?" On the other hand I have a suite of classes though based on MFC do the job. This is code that is written and works. Having written the code for the row labels in the timeline I started working on the column (or timeline) header. Starting work on this I used the existing class as a guide for functionality etc. The further I worked with this the less certain I became. For example selection in this view is handled within the Cello engine and just reflected in the view. This is currently encapsulated in a C++ class. I can either
  • Wrap this in a cocoa class:
  • Serialize it into an NSDictionary
  • Project the C++ class directly into cocoa.
My approach with this project has been to aspire to the path of least resistance. This is to try and get the project up and running as quickly as reasonably possible, but also I have no desire to rewrite things that work. The original Cello source code is very well written, lovingly commented - why not use what I can?

So my revised approach is to use what I can of the MFC in the user interface. So what was a SOKTimeLineView that is an MFC based class is now an CHTimeLineView a subclass of NSView. The bulk of the methods I am rewriting as Cocoa in Objective C++. This is simply not hard - just copy and paste in the methods and fiddle them about. If I specify the views as having flipped coordinates even the coordinate calculations for drawing are more or less right.

As with all of these things there is something to loose - and what is to loose? Well mixing C++ and Objective-C is a bit of a nasty thing. Under 32-bit there are two incompatible exception models. So I will have to tread with care and make sure that I don't end up propagating C++ exceptions through AppKit. The other thing to loose is the work I have done - but the reality is that it will not be entirely lost. I imagine that the bulk of what I have written I can simply transplant.

Saturday, December 26, 2009

TimeLine view - rollovers

Each row of the TimeLine header view has two areas that are active. One is the drawMode and has a popup - the other represents the locked status of an item. These appear as graphics in the TimeLine header. I have decided to make these active areas distinct by making them behave as rollovers. The effect that I am looking for is quite simple - as the mouse goes over the active area I want to draw a grey oval. This will also provide an opportunity to draw a popup arrow for the popup. Adding the arrow so that it always draws makes the header look cluttered - only drawing this in the mouse-over state will make this a little cleaner.

The simplest way I found to do this is to use the NSTrackingArea. My aim was to add a NSTrackingArea for each visible active area.

To do this I added an updateTrackingAreas method to the view - in here I dispose of any existing tracking areas, and then to loop through all of the visible rows and for each to add a tracking area for each of the active areas on the row. When you construct a tracking area you pass in a NSDictionary of userInfo - here I pass in details of the row and the particular active area it represents. The updateTrackingAreas method is called when the header view scrolls or is resized - for the moment this is enough.

When you establish tracking areas on a view mouseEntered and mouseExited methods of your view will be called as the mouse enters and exits the view. here you need to get the relevant areas to redraw - I affected a method setNeedsDisplayInRow that is little more that a sprinkle of sugar for setNeedsDisplayInRect.

My guess is that the procedure for getting a rollover to work for a cell in in an NSTableView would be pretty much the same - though you might need to updateTrackingAreas in more situations (for example when table rows are resized).

Currently the drawing is still handled by the header view - but I plan to move this into a NSCell subclass.
An active area with a rollover.

Friday, December 18, 2009

TimeLine view - making the header active

In the TimeLine view the header is not a passive information-only structure it is active.
  • The text is editable.
  • The "Lock" icon works as a checkbox.
  • The "Draw Mode" icon is a popup menu with four states.
This is what one of the popup menus looks in the windows version if Cello.


The way that I plan to accomplish these active areas are by using subclasses of NSCell and friends. There is, for example, a NSPopupButtonCell that handles a popup menu button.

NSCell and it's derivatives are classes that encompass everything that is needed to draw a particular object, say a popup menu, and handle it's behavior. It is everything except the view in which it is drawn. As with all things in Cocoa they are there for the taking - you can use them as is or subclass them as you please. The way you use them is really easy - if you want them to draw then you:
  • Set it up
  • Ask it to draw in a given rectangle in a given view
If you want to handle a mouse down then you:
  • Set it up
  • Ask it to handle the mouse down
All the Cocoa controls (for example NSButton) use a NSCell subclass to do the drawing etc. If you want to see how they work - check out the Cocoatron project. You can download the source and see just how simple the cocoatron NSButton is.

I have started by crudely hacking in the NSPopupButtonCell
into the TimeLine view and getting it to draw and to handle the mousedown - the menu is just a dummy. My plan is to refine this (stepwise refinement) until I get what I want.

Sunday, December 13, 2009

TimeLine view - header with icons

I have added the drawing of icons to the header row. I have taken the existing graphics from the PC version and quite quickly converted them to individual tiff images in photoshop. I am for the moment just reusing the PC images. When Cello is finished they will need reworking as they don't have that OSX feel. My thought is to hand this part of the project over to someone else - it really is something to worry about another day.

The drawing of icons is quite straight forward. I just figure out the point to draw the icon so it is centered in its rectangle and call compositeToPoint:operation and that is about it.
If you look closely you can see that there is a white bloom (square) around the icons - this is an actual transparency issue with the graphics (something wrong with my photoshop efforts).