Sunday, November 15, 2009

NSDocumentController - Maintaining a single document

I have been trying to figure out the best way to structure things to support the concept of the "main frame". Under Windows applications generally seem to have a main frame. This is the single key window that is central to the application. The OSX world is different - you generally have multiple windows one for each document. As a Mac user, now working windows much of the time. I find the paradigm a little strange at first. That said once you are accustomed it is fine. Also as I have mentioned before it is something that you see in some consumer apps on OSX so I am keeping this behaviour.

The trick with these things is, as ever, to find the path of least resistance. Now that I am working with Cocoa I want to do the same on the Cocoa end. I don't want to fight AppKit I want to dovetail in as well as I can so that I have to do as little as possible.

Looking at AppKit I definitely want to retain an NSDocument. I want this as NSDocument hooks into the undo manager really nicely. I will have to somehow mesh Cello's undo manager with NSDocument but that is something that I will look at later. The NSDocument as support for all the open and saving and I can hook into this.

So what I have done is write my own subclass of NSDocumentController - this is the master controller for all of my documents (the thing is I only ever have one). So my sub class will ensure that there is just one document - so if you open a document it will close the existing one.

I started by adding two methods to my document controller

- (void)addDocument:(NSDocument *)document {
[super addDocument:document];
}

- (void)removeDocument:(NSDocument *)document {
[super removeDocument:document];
}

So I could add breakpoints to check where these things were called. I could add breakpoints using GDB to the NSDocumentController methods - but I like this way. I wrote a new method that closeAllDocuments to close all of the documents (there should only ever be one). And then call that at appropriate times.
  • - (IBAction)newDocument:(id)sender
  • - (IBAction)openDocument:(id)sender
Finally I made my document controller my application delegate (if I need to I can always change this) so that I could implement the two methods:
  • - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
  • - (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
And that takes care of the best part of it.

While figuring this out I came accoss the following post that proved very usefull here. The post is in Japanese which I was not able to follow - but the code was usefull.

Wednesday, November 11, 2009

Kon and Bal - NSWindowController won't show my window

I am trying to rationalized the Windows idea of a MainFrame with the Cocoa document model, and in that deal with the meshing of C++ and Cocoa. Out the box Cocoa will give you a window per document - but infinitely configurable cocoa gives you the tools to skin the cat as you want it.

What I have been trying to do is get my C++ MainFrame to create a NSWindowController (subclass) and to create and show the window. This sounds easy enough - and indeed should be. The steps are like this:
  • Add a nib with the window and the FileOwner being the window controller.
  • Add a call to [myController initWithWindowNibName:@"NibName"] to load the nib.
  • Show the window with [myController showWindow:blah]
But nothing happened. The window did not show.

I had a problem like this six or eight months ago with another project I was working on and I had not connected the window to the controller in my nib. Sure enough there it was. But the problem did not go away.

In the debugger I could see that the controller had a window and that window's flags showed that it had become visible - but still no window was visible. Once I saw the window for the briefest moments - but flipping windows when messing with the debugger made it unrepeatable.

In the end I went back to an old technique of debugging that I fall back to when I can't fathom things like this. I created a subclass of NSWindow and overrode the alloc, dealloc and some other key methods, and changed the nib so that my window was of this type. In the implementation of my TestWindow class I did no more than called the parent method. What this did was allow me to put breakpoints on the window's key life cycle moments. What was going on in my head was the thought that either that somehow:
  • I was not creating a window at the right moment (what this might mean I did not know)
  • The window was not being shown - the calls that would show the window where not being made.
  • The window was being disposed immediately after I had created it.
As few moments in the debugger and I could see that my dealloc was being called. The controller was being disposed in an AutoReleasePool - I had an owner count problem.

BAL: Nasty.

KON: Yeah.

Monday, November 2, 2009

Meshing Cocoa with C++

One of the key issues I am looking at is the problem of having a code base that is written as C++ classes and meshing that with an application framework that is written in Objective C. My rough plan at the moment is to have a parallel Objective C object for each C++ object that appears directly in the UI. So, for example, there is an C++ object in Cello that is what is called the FrameView. This is one of the central classes that appears deep through much of the cello code. My plan is to split this into two

  • IMainFrame - a C++ subclass
  • CHMainFrame - an Objective-C wrapper class
I don't imagine the Objective-C wrapper to be a simple wrapper. What I imagine is that it will present what is needed of the C++ class to the objective C world in doing this there will be things that make sense to be written here.

Sunday, November 1, 2009

Your document contents here


This is a screen shot of the first file that has been opened by the cello application. The application will open a cello document that has been created in the PC version of cello. It openes the document much as the test application did but it is wired to the beginnings of a "propper" application.

Sunday, October 18, 2009

Debugging Individual CPlusTestRig Test Cases

Having got the unit tests working in the debugger I don't really want all the tests to be executed each time I run the debugger. I just want to work with the problem test.

The easiest method I know of is just linking in the tests that you want. It is what I have used in the past when fixing unit tests (prior to getting the debugger to work I used NSLog calls and just looked at the output - my programming start predates symbolic debuggers and if you go old school you can still find bugs and fix things).

I have named all my tests files so that they end in _test and then the file suffix. This means that I can: 


  1. Select all my test files in the search widget in XCode by typing _test.cpp and then in the main area choose the tests that I do not want
  2. Click on the Info button in the tool bar to get info on all of the tests
  3. Select the Targets pain in the info window and turn off the tests that I don't want to run.


As I have boith .cpp and .mm files this is actually a two stage process - but it is very simple to do.

Wednesday, October 14, 2009

Debugging CPlusTestRig UnitTests

Having started work on the user interface I am doing a fair bit of reorganizing. In this reorganization I have been changing the way that UI related components are being registered. What has happened is that in the early stages of development I had roughed-out stubs installed as the needed UI components - but these no longer load. The upshot of this is that I have made the first progress am ready to commit my changes to the library but my unit tests are failing - components are missing. I am getting crashes.

Pinning down these sort of problem with a debugger is generally quite straight forward. My frustration is that out of the tin the debugger does not "just work". OSX has made me lazy - I am used to things just working.

The best set of hints I have found on this are on Chris Hanson's blog - this is for testing cocoa frameworks. I am using CPlusTestRig but there is enough there to figure out what you need to do. This is my recipe


Turn off the automatic running of unit tests.


I did this as I wanted my unit test related files to build but I did not want the shell scripts that executes them to run. If it does run it will run outside of the debugger. To do this:
  • Selected the Unit Test target in the files
  • Choose get info from the tool bar
  • Put a hash (#) in front of the line "${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests" this has the effect of commenting it out. If you do it this way you can remove the hash character and be back where you were.

Create a new Executable 


  • Choose Project>New Custom Executable
  • Give it a name (I choose "Debug UnitTests") and enter "Tools/CPlusTestRig" in the Executable path
  • In the window that appears click on the Arguments Tab and add in the name of your test bundle - if you have spaces in your name you need to escape them

Run


  • Set your active executable.
  • Hit the debug button on your toolbar.


Saturday, July 25, 2009

Fun and games with owner counts

Working on the UI my current short-term target is to open a file. The results of this will not be ostensibly much different from the test bed application which could open, save and publish a file except that it involves putting in the beginnings of a skeleton for the UI of the final app. The skeleton is a mix of Cocoa C++. There are C++ subclasses of existing Cello objects and Cocoa wrappers.


I don't like checking things into the project that don't compile and in some sense work. I view checkins as a progression forward so working on my next checkin I have been dealing with owner counting problems. 


Cello predates boost, or at least does not have any boost in it and owner counting is done through inheritance from custom classes and hand-rolled templates. This works fine but there have been some wrinkles with classes that use the 'interface' extension (which is I believe a Microsoft extension). I have replaced interfaces with straight subclasses (for the moment at lease) and in this there have been uninteresting wrinkles with the owner counting mechanisms. Just uninteresting stuff that you need #ifdef your way through.