Monday, November 23, 2009

TimeLine view - first brush strokes

I have started implementing the TimeLine view - and implementing it from the ground up. My approach is to start with the basic topology and then to add flesh to the bones. The PC view has row labels of user determined width. I can kick this off in a small sample project - I should be able to get the basic structure of what I need working outside of Cello. So I started by adding a new (temporary) target to XCode and sketched out a basic splitter with two views in interface builder.

The drawRect methods of the views just fill the two rectangles with grey.

As a next step I have broken the two views into several sub views. The row header, column header and content are all need to be within NSScrollViews. So I have added those.

On from this I have started sketching out the protocol for the DataSource modeling it roughly on the DataSource in the NSTableView. My idea is to start with a protocol that is close to how I am implementing the TimeLine - initially something very much like a table. My plan is then when the basic table functionality is working to shift directions and refine the protocol so it more closely resembles the data that is presented and required by Cello as it stands.

Sunday, November 22, 2009

NSTableView vs MBTableGrid - the verdict

I have spent some time looking at NSTableView and also the open source MBTableGrid. What I have been doing is playing around with them in test applications to try and get a measure of the two approaches. In the end I have decided to write my own view from the ground up. Looking at the MBTableGrid example has given em a really good idea of what it will take to do it - and the work will not be greater from what I will need to do to get MBTableGrid customized and wrinkled into the form that I need. MBTableGrid is closer to what I need than NSTableView.

Thursday, November 19, 2009

NSTableView vs MBTableGrid - the trial

The main cello UI is comprised of three views, FrameView TimeLine and ActionView. Together these views comprise the majority of the editing functionality. My intention was to start with the FrameView. The FrameView is used to draw the the items at a given point in time. So when you sketch, say, a box you do this in the FrameView. It seemed like the most important view to start with. There is, however a really close linkage between the views - so starting with the TimeLine view seems the simplest. We shall see:

In the PC version the time line looks like this:

It is more-or-less a list of a single column with row and column headings. The row headings contain active controls - as do the rows themselves. The column heading is a has the time-ruler and is also active.

As far as implementing this in cocoa I can see two choices
  • Implement this strange list like thing myself
  • Use the cocoa NSTableView
The best course does not seem immediately obvious to me. One of the benefits of writing a blog is that when writing things you can think about them in a slightly different way. One of the things that occurred to me while writing this is that the table is not actually a table of a single column - it can be thought of as a table of many many columns. Each time interval (typically something like 1/15th of a second) can be thought of as a single column - this would seem to offer some simplifications in the implementation.

In trying to determine the best course of action I have tried to enumerate the various things that need to be done with the time line.
  • Items in the row header need to be editable.
  • Items there are buttons and popup menus in the row headers
  • Rows need to be selectable
  • Cells have items that can be added/deleted and removed
  • Some items can stretch over more than one cell
  • Items that stretch over more than one cell appear as a "whole" - they are drawn as a whole
  • There is a row (the sound track row) that does not (currently) have any editable elements.
  • The width of the row area needs to be resizable
  • The header area is not a standard table header
Looking at this I can try and figure out what the best way forward should be. Also into the mix comes MBTableGrid - a set of classes that draws a "Spread Sheet" type widget written by Matt Ball - that could be a basis for a non-list approach. The Author has solved a number of the problems that I would face - there is a separate row/column area, it supports inline editing. The code can be found here.

The NSTable approach has a few stumbling blocks:
  • Tables don't have row headers - this could be be implemented by a separate parallel - list. you just have to sync the scrolling and selection.
  • I would need a different header - fortunately the table header can be sub-classed.

Monday, November 16, 2009

NSDocumentController - Reusing a single window across multiple documents

A further twist to having a single document visible at any one time is that I would like to keep a single window and reuse it. The way I have done this is as follows
  • When I get the NSApplicationWillFinishLaunchingNotification I create the window controller and window. I do this by reading in a nib file that has my subclass of a window controller and a window.
  • In the makeWindowControllers method of my document subclass I call addWindowController to add my global window controller.
And that is about it.

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.