Saturday, December 26, 2009
TimeLine view - rollovers
Friday, December 18, 2009
TimeLine view - making the header active
- The text is editable.
- The "Lock" icon works as a checkbox.
- The "Draw Mode" icon is a popup menu with four states.
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.
- Set it up
- Ask it to draw in a given rectangle in a given view
- Set it up
- Ask it to handle the mouse down
Sunday, December 13, 2009
TimeLine view - header with icons
Saturday, December 12, 2009
Cocoa and Drawing truncated text
TimeLine view - header regions
Friday, December 4, 2009
NSScrollView - problems
[super resizeWithOldSuperviewSize:oldSize];
[self reloadData];
}
- (void)reloadData
{
.... stuff
NSRect contentRect = NSMakeRect(0, 0, _numberOfColumns * _columnWidth, _numberOfRows *_rowHeight);
contentRect.size.height = MAX(contentRect.size.height, [[self superview] bounds].size.height);
[contentView setFrameSize:contentRect.size];
.... stuff
}
Thursday, December 3, 2009
TimeLine - sketching out more
Monday, November 23, 2009
TimeLine view - first brush strokes
Sunday, November 22, 2009
NSTableView vs MBTableGrid - the verdict
Thursday, November 19, 2009
NSTableView vs MBTableGrid - the trial
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
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
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
- 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.
Sunday, November 15, 2009
NSDocumentController - Maintaining a single document
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
- - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename
- - (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
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
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]
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.
BAL: Nasty.
KON: Yeah.
Monday, November 2, 2009
Meshing Cocoa with C++
- IMainFrame - a C++ subclass
- CHMainFrame - an Objective-C wrapper class
Sunday, November 1, 2009
Your document contents here
Sunday, October 18, 2009
Debugging Individual CPlusTestRig Test Cases
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).
- 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
- Click on the Info button in the tool bar to get info on all of the tests
- Select the Targets pain in the info window and turn off the tests that I don't want to run.
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.
Monday, June 29, 2009
UI - Initial Approach
Having got to the stage where I can open and publish test files I now have start the serious work of the user interface. My current thoughts are to start with the existing view hierarchy and to use it to wrap cocoa views. For example there is a time-line view. This view manages a time-line and the events that exist on it. There is a time-line base class as well as sub-classes, there are commands that are originated from the time-line etc. The path of least resistance would seem to be to keep this structure and to wrap cocoa views behind the scenes get the meld that way.
Wednesday, March 25, 2009
XCode - Development Version Numbers
To do this I did the following:
- Added a new file history.xconfig to the project.
- In history.xconfig added a single line CELLO_BUILD_NUMBER=0129
- Changed the build settings so that they are "based on history.xconfig". You do this by choosing the build settings from a the "based on" popup that appears in the build settings pane.
- Changed the Product Name in the build settings to Cello $(CELLO_BUILD_NUMBER)
The effect of this is each time I change history.xconfig Cello will be built with the appropriate build number.
Sunday, March 22, 2009
The Joy of GIT
To install Git on your machine the easiest way to go is to use MacPorts. MacPorts is a cunning system that will install a package and all it's dependent packages, downloading, building and linking as necessary like magic. Once you have installed MacPorts installing a package like GIT is a breeze. Pop open the terminal and type
$sudo port install git-core
To import a CVS project into GIT you will need to install cvsps. Again this can be done with MacPorts
$sudo port install cvsps
And you are good to go, well to migrate your CVS repository to GIT. For me the following command imported the project in its entirety
$git cvsimport -v -C path_to_my_git_repositry my_module
Tuesday, March 3, 2009
UI - BabySteps
- Small steps are easier
- I have a tiny amount of spare time
Sunday, January 25, 2009
UI - Preparing for Power
So for my part I am "Preparing for UI". These are the small baby steps that are required to start doing something. I have split the project into a library that has most of the blood-and-guts (the engine) and the test application links against it. Following this I have created a new target (that is really a copy of the test Application) called Cello. This will be the finished Application and this is where the UI will live. I have always found XCode an awkward beast to handle so adding targets is a surprisingly cumbersome to manage. XCode is not a bad tool but for me, and it may well be a personal short coming, it seems to fly against the idea that "simple things should be accomplished simply".
Wednesday, January 7, 2009
Re-factoring
The main things that I am doing re-factoring is:
- Working through consistent naming.
- Wrapping most the core foundation functions as methods.
typedef const struct __CFData * CFDataRef;What ACS did was to then define the anonymous __CFData and add methods to it. So to add a method that would wrap CFDataGetLength it would did something like this:
typedef struct __CFData * CFMutableDataRef;
struct __CFDataand then defined an implementation for GetLength in terms of CFDataGetLength
{
CFIndex GetLength() const;
};
inline CFIndex __CFData::GetLength() constWhen I started work on the Cello project I actively resisted this in favor of calling (typically) core foundation directly. What I found was that it made life a lot easier if I wrapped all the 'create' or 'copy' functions so that they returned boost::intrusive_ptr wrapped CF types. I did this as I used boost::intrusive_ptr to take care of all the ownership/ownercounting issues. When working on the Sound part of Cello I 'rediscovered' this ACS style wrapping, partly because it just made the code easier to read.
{
return CFDataGetLength(this);
}
It means that instead of calling:
int length = CFDataGetLength(myData);You write:
int length = myData->GetLength();If you are dealing with boost::intrusive_ptr it eleminates a lot of 'get' calls so instead of
int length = CFDataGetLength(myData.get());You write:
int length = myData->GetLength();The other thing that comes into play is that I have had to wrap small amounts of Objective-C so that it can be used in C++. I have opted to keep Objective C out of the general C++ not just to properly isolate the complete madness of two different incompatible types exception handling but also to keep things simpler. It is generally natural to express these as C++ methods