I am trying to nest NSCollection view inside of one another. I have tried to create a new project using the Apple Quick Start Guide as a base.
I start by inserting a collection view into my nib, to the view that is automatically added I drag another collection view onto it. The sub-collection view added gets some labels. Here is a picture of my nib:
I then go back and build my models:
My second level model .h is
#interface BPG_PersonModel : NSObject
#property(retain, readwrite) NSString * name;
#property(retain, readwrite) NSString * occupation;
#end
My First level model .h is:
#interface BPG_MultiPersonModel : NSObject
#property(retain, readwrite) NSString * groupName;
#property(retain,readwrite) NSMutableArray *personModelArray;
-(NSMutableArray*)setupMultiPersonArray;
#end
I then write out the implementation to make some fake people within the first level controller(building up the second level model):
(edit) remove the awakefromnibcode
/*- (void)awakeFromNib {
BPG_PersonModel * pm1 = [[BPG_PersonModel alloc] init];
pm1.name = #"John Appleseed";
pm1.occupation = #"Doctor";
//similar code here for pm2,pm3
NSMutableArray * tempArray = [NSMutableArray arrayWithObjects:pm1, pm2, pm3, nil];
[self setPersonModelArray:tempArray];
} */
-(NSMutableArray*)setupMultiPersonArray{
BPG_PersonModel * pm1 = [[BPG_PersonModel alloc] init];
pm1.name = #"John Appleseed";
pm1.occupation = #"Doctor";
//similar code here for pm2,pm3
NSMutableArray * tempArray = [NSMutableArray arrayWithObjects:pm1, pm2, pm3, nil];
return tempArray;
}
Finally I do a similar implementation in my appdelegate to build the multiperson array
- (void)awakeFromNib {
self.multiPersonArray = [[NSMutableArray alloc] initWithCapacity:1];
BPG_MultiPersonModel * mpm1 = [[BPG_MultiPersonModel alloc] init];
mpm1.groupName = #"1st list";
mpm1.personModelArray = [mpm1 setupMultiPersonArray];
(I'm not including all the code here, let me know if it would be useful.)
I then bind everything as recommended by the quick start guide. I add two nsarraycontrollers with attributes added to bind each level of array controller to the controller object
I then bind collectionview to the array controller using content bound to arrangedobjects
Finally I bind the subviews:
with the grouptitle label to representedobject.grouptitle object in my model
then my name and occupation labels to their respective representedobjects
I made all the objects kvo compliant by including the necessary accessor methods
I then try to run this app and the first error I get is: NSCollectionView item prototype must not be nil.
(edit) after removing awakefromnib from the first level model I get this
Has anyone been successful at nesting nscollection views? What am I doing wrong here? Here is the complete project zipped up for others to test:
http://db.tt/WPMFuKsk
thanks for the help
EDITED:
I finally contacted apple technical support to see if they could help me out.
Response from them is:
Cocoa bindings will only go so far, until you need some extra code to make it all work.
When using arrays within arrays to populate your collection view the
bindings will not be transferred correctly to each replicated view
without subclassing NSCollectionView and overriding
newItemForRepresentedObject and instantiating the same xib yourself,
instead of using the view replication implementation provided by
NSCollectionView.
So in using the newItemForRepresentedObject approach, you need to
factor our your NSCollectionViewItems into separate xibs so that you
can pass down the subarray of people from the group collection view to
your inner collection view.
So for your grouped collection view your override looks like this:
- (NSCollectionViewItem *)newItemForRepresentedObject:(id)object
{
BPG_MultiPersonModel *model = object;
MyItemViewController *item = [[MyItemViewController alloc] initWithNibName:#"GroupPrototype" bundle:nil];
item.representedObject = object;
item.personModelArray = [[NSArrayController alloc] initWithContent:model.personModelArray];
return item;
}
And for your inner collection subclass your override looks like this:
- (NSCollectionViewItem *)newItemForRepresentedObject:(id)object
{
PersonViewController *item = [[PersonViewController alloc] initWithNibName:#"PersonPrototype" bundle:nil];
item.representedObject = object;
return item;
}
here is a sample project that they sent back to me -
http://db.tt/WPMFuKsk
I am still unable to get this to work with my own project. Can the project they sent back be simplified further?
Please take a closer look at this answer
Short answer:
Extracting each NSView into its own .xib should solves this issue.
Extended:
The IBOutlet’s specified in your NSCollectionViewItem subclass are not connected when the prototype is copied. So how do we connect the IBOutlet’s specified in our NSCollectionViewItem subclass to the controls in the view?
Interface Builder puts the custom NSView in the same nib as the NSCollectionView and NSCollectionViewItem. This is dumb. The solution is to move the NSView to its own nib and get the controller to load the view programmatically:
Move the NSView into its own nib (thus breaking the connection between the NSCollectionViewItem and NSView).
In I.B., change the Class Identity of File Owner to the NSCollectionViewItem subclass.
Connect the controls to the File Owner outlets.
Finally get the NSCollectionViewItem subclass to load the nib:
Usefull links:
how to create nscollectionview programatically from scratch
nscollectionview tips
attempt to nest an nscollectionview fails
nscollectionview redux
Related
This is my BookListObject.h:
# property (nonatomic,retain)NSImage *bookImage;
# property (nonatomic,copy) NSString *bookName;
and I connect these bindings:
bookImage ----- representedObject.bookImage
bookName ------representedObject.bookName
arrayController-------arrangedObject
-(void)awakeFromNib{
BookListObject * book=[[BookListObject alloc] init];
book.bookName=#"unknown";
book.bookImage=[NSImage imageNamed:#"dis"];
_bookList=[NSMutableArray arrayWithObjects:book, nil];
[arrayController addObject:_bookList];
}
I have seen this video.
When I run my project, it crashes and prompts that NSCollectionView item prototype must not be nil.
I found the following method, but it doesn't work:
NSCollectionViewItem *itemPrototype = [self.storyboard instantiateControllerWithIdentifier:#"collectionViewItem"];
self.bookListCV.itemPrototype=itemPrototype;
The problem remains. What should I do? How can I bind the collectionView with collectionViewItem in the storyboard? I think XCode6 supports it only when you use xib.
I have a problem with a #sum binding of a column in my program:
I'm doing a Coredata, NSPersistentDocument based program. I'm doing mostly everything from IB, the creation of the data model, NSArrayController and NSTableView...
I have just 1 entity with 62 attributes (61 NSString and 1 NSNumber). I import a CSV file with 12722 records. Import works well, can save to xml, binary, sqlite... I've double checked that the overall process works perfect. Can save/load. Everything is there.
The problem that I have: I've created a label that I BIND to #sum of the column with the NSNumber property. This is how I did
> label->Bindings Inspector->Value
> Bind to: My_Entity_NSArrayController
> Controller Key: selection
> Model Key Path: #sum.myNumericAttribute
When I run the program, click on Import, Select ALL the rows, the #sum works well. It's fast, however and here is the first problem: once I save the file (tried all... binary/xml/sqlite) and later load it and try to Select ALL again, the program crash without error.
Tried through "Profile"->Allocations. I noticed:
I don't have memory leaks
When loading from disk and then select all: Goes extremelly slow. After 5 minutes didn't yet finished (I stopped it) and I saw +45MB of CFNumber (Live Bytes) and >1.500.00#Overall. So, something is wrong here, as I'm talking about 12722 rows/registers of type Interger32.
The second problem is the same but reproduced from a different angle. Instead of using "selection" I've tried to use "arrangedObjects".
In this case the problem appears even while importing from CSV, it goes extremely slow and it finally crash. Trying to open an already created file also crash.
This is how I did label->Bindings Inspector->Value
> label->Bindings Inspector->Value
> Bind to: My_Entity_NSArrayController
> Controller Key: arrangedObjects
> Model Key Path: #sum.myNumericAttribute
Can you please help me with some light on what to look for or ideas that can help me find where the problem is?.
Thanks a lot.
Luis
---- NEW EDIT AFTER MORE RESEARCH ----
I've found a workaround which I DONT' UNDERSTAND, please comments/answers really appreciated.
My program uses Coredata (SQLite), NSPersistentDocument, NSTableView and an NSArrayController. I want to have a working NSTextField bound to a #sum Collection Operation
Problem: As soon as I open an existing document with SQLite DB populated and I try to bind to the arrangedObjects.#sum.t_24_Bookings from the NSWindowController, the program crash.
My initial guess it's related to the Cannot access contents of an object controller after a nib is loaded however I've followed the recommendation of performing a first Fetch like this without success:
- (void) awakeFromNib
{
:
BOOL ok = [[self out_CtEn_Transaction] fetchWithRequest:nil merge:NO error:&error];
:
Continuing with this idea I've found that if I create a "real" complete Fetch + I perform a #sum access from the Document subclass, then it works.
Here is the code with comments I've put in place in order to have the workaround working.
ABDocument interface (a NSPersistentDocument subclass)
#interface ABDocument : NSPersistentDocument {
BOOL ivNewDocument;
NSArray *ivFetchedTransactions;
NSNumber *ivTotalBookings;
}
#property (nonatomic, getter=isNewDocument) BOOL newDocument;
#property (nonatomic, retain) NSArray *fetchedTransactions;
#property (nonatomic, retain) NSNumber *totalBookings;
:
ABDocument implementation
#import "ABDocument.h"
#import "ABWindowController.h"
#implementation ABDocument
#synthesize newDocument = ivNewDocument;
#synthesize totalBookings = ivTotalBookings;
#synthesize fetchedTransactions = ivFetchedTransactions;
:
/** #brief Create one instance of my custom NSWindowController subclass (ABWindowController)
*
* In my NSPersistentDocument I do override makeWindowControllers, where I create
* one instance of my custom NSWindowController subclass and use addWindowController:
* to add it to the document.
*
*/
- (void) makeWindowControllers
{
// Existing Document?
if ( ![self isNewDocument]) {
// NSLog(#"%#:%# OPENING EXISTING DOCUMENT", [self class], NSStringFromSelector(_cmd));
// Opening existing document (also has an existing DDBB (SQLite)), so
// make sure I do perform a first complete "fetch + #sum" to void issues
// with my NIB bind's.
[self firstFetchPreventsProblems];
}
// Now I can create the Window Controller using my "MainWindow.xib".
ABWindowController *windowController = [[ABWindowController alloc] init];
[self addWindowController:windowController];
[windowController release];
}
/** #brief First complete "fetch + #sum" to void issues with my NIB bind's.
*
* Before I create the Window Controller with "MainWindow.xib" I have to perform a
* first Fetch AND also retrieve a #sum of an NSNumber column.
*
* My NIB has an NSTextField BOUND to #arrangedObjects.#sum.<property> through a NSArrayController
* If I don't call this method before the NIB is loaded, then the program will crash.
*
*/
- (void) firstFetchPreventsProblems {
// Prepare the Fetch
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Transaction"];
// 1) Perform the Fetch
NSError *error = nil;
[self setFetchedTransactions:[[self managedObjectContext ] executeFetchRequest:request error:&error]];
if ([self fetchedTransactions] == nil)
{
NSLog(#"Error while fetching\n%#",
([error localizedDescription] != nil) ? [error localizedDescription] : #"Unknown Error");
exit(1);
}
// 2) Execute Collection Operation #sum
[self setTotalBookings:[[self fetchedTransactions] valueForKeyPath:#"#sum.t_24_Bookings"]];
}
ABWindowController (The controller that loads my NIB)
- (void)windowDidLoad
{
:
// PROGRAM CRASH HERE
// IF [self firstFetchToPreventsProblems]; is NOT CALLED
// ABDocument's "makeWindowControllers:"
[[self totalSumField] bind: #"value" toObject: [self out_CtEn_Transaction]
withKeyPath:#"arrangedObjects.#sum.t_24_Bookings" options:nil];
}
Please If you can comment really appreciated, I've got a solution but I don't understand why.
Tanks,
Luis
I found the problem myself after several days researching. It was easy (now that I know):
In parallel I was creating a secondary thread and happened that I was accessing the data model from two different threads. As it's been explained in several Q&As here in Stackoverflow, it's very dangerous.
I've applied the commented solutions in several posts of creating a secondary MOC in the secondary thread.
Now my code is thread safe as per coredata related actions, so program is not crashing.
Thanks again to the community.
Luis
I am writing an app which is a sort of dictionary - it presents the user with a list of terms, and when clicked on, pops up a dialog box containing the definition. The definition itself may also contain terms, which in turn the user can click on to launch another definition popup.
My main app is stored in 'myViewController.m'. It calls a custom UIView class, 'CustomUIView.m' to display the definition (this is the dialog box that pops up). This all works fine.
The text links from the CustomUIView then should be able to launch more definitions. When text is tapped in my CustomUIView, it launches another CustomUIView. The problem is, that this new CustomUIView doesn't have access to the hash map which contains all my dictionary's terms and definitions; this is only available to my main app, 'myViewController.m'.
Somehow, I need to make my hash map, dictionaryHashMap, visible to every instance of the CustomUIView class. dictionaryHashMap is created in myViewController.m when the app opens and doesn't change thereafter.
I don't wish to limit the number of CustomUIViews that can be opened at the same time (I have my reasons for doing this!), so it would be a little resource intensive to send a copy of the dictionaryHashMap to every instance of the CustomUIView. Presumably, the solution is to make dictionaryHashMap a global variable.
Some of my code:
From myViewController.m:
- (void)viewDidLoad
{
self.dictionaryHashMap = [[NSMutableDictionary alloc] init]; // initialise the dictionary hash map
//... {Code to populate dictionaryHashMap}
}
// Method to pop up a definition dialog
- (void)displayDefinition:(NSString *) term
{
NSArray* definition = [self.dictionaryHashMap objectForKey:term]; // get the definition that corresponds to the term
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
[definitionPopup setTitle: term];
[definitionPopup setMessage: definition];
[definitionPopup show];
}
// Delegation for sending URL presses in CustomUIView to popupDefinition
#pragma mark - CustomUIViewDelegate
+ (void)termTextClickedOn:(CustomUIView *)customView didSelectTerm:(NSString *)term
{
myViewController *t = [[myViewController alloc] init]; // TODO: This instance has no idea what the NSDictionary is
[t displayDefinition:term];
}
From CustomUIView.m:
// Intercept clicks on links in UIWebView object
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
[myViewController termTextClickedOn:self didSelectTerm:request];
return NO;
}
return YES;
}
Any tips on how to make the dictionaryHashMap visible to CustomUIView would be much appreciated.
I have tried making the dictionaryHashMap global by doing the following:
Changing all instances of 'self.dictionaryHashMap' to 'dictionaryHashMap'
Adding the line 'extern NSMutableDictionary *dictionaryHashMap;' to CustomUIView.h
Adding the following outside of my implementation in myViewController.m: 'NSMutableDictionary *dictionaryHashMap = nil;'
However, the dictionaryHashMap remains invisible to CustomUIView. As far as I can tell, it actually remains a variable which is local to myViewController...
It's not resource-intensive to pass around the reference (pointer) to dictionaryHashMap. A pointer to an object is only 4 bytes. You could just pass it from your view controller to your view.
But I don't know why you even need to do that. Your view is sending a message (termTextClickedOn:didSelectTerm:) to the view controller when a term is clicked. And the view controller already has a reference to the dictionary, so it can handle the lookup. Why does the view also need a reference to the dictionary?
Anyway, if you want to make the dictionary a global, it would be more appropriate to initialize it in your app delegate, in application:didFinishLaunchingWithOptions:. You could even make the dictionary be a property of your app delegate and initialize it lazily.
UPDATE
I didn't notice until your comment that termTextClickedOn:didSelectTerm: is a class method. I assumed it was an instance method because myViewController starts with a lower-case letter, and the convention in iOS programming is that classes start with capital letters. (You make it easier to get good help when you follow the conventions!)
Here's what I'd recommend. First, rename myViewController to MyViewController (or better, DefinitionViewController).
Give it a property that references the dictionary. Whatever code creates a new instance of MyViewController is responsible for setting this property.
Give CustomUIView properties for a target and an action:
#property (nonatomic, weak) id target;
#property (nonatomic) SEL action;
Set those properties when you create the view:
- (void)displayDefinition:(NSString *)term {
NSArray* definition = [self.dictionaryHashMap objectForKey:term];
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
definitionPopup.target = self;
definitionPopup.action = #selector(termWasClicked:);
...
In the view's webView:shouldStartLoadWithRequest: method, extract the term from the URL request and send it to the target/action:
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
NSString *term = termForURLRequest(request);
[self.target performSelector:self.action withObject:term];
return NO;
}
return YES;
}
In the view controller's termWasClicked: method, create the new view controller and set its dictionary property:
- (void)termWasClicked:(NSString *)term {
MyViewController *t = [[MyViewController alloc] init];
t.dictionary = self.dictionary;
[t displayDefinition:term];
}
Create a class that will be used as singleton. Example.
You Should always keep your data in separate class as the mvc pattern suggest and that could be achieved by using a singleton class for all your dictionary terms and accesing them from every custom view when needed.
I have an NSArray of custom NSObjects. Each object has some properties and an image that I would like to display in a grid view. NSMatrix appears to be a good solution to my problem, but I am having issues getting the content of the objects to display.
Couple of things to note.
I am not using core data
I am trying to do this programmatically
I have considered using NSCollectionView but NSMatrix appears to be a better solution in this case
All the cells follow the same display format as each other - i.e. I'm not wanting to pass different cells different types of objects, just a different instance of the object
Assume I have an NSView (matrixContainerView) in a window. The controller file has an IBOutlet to matrixContainerView. In my controller I have the following in my awakeFromNib:
NSMatrix* matrix = [[NSMatrix alloc]
initWithFrame:[matrixContainerView bounds]
mode:NSRadioModeMatrix
cellClass:[MyCustomCell class]
numberOfRows:5
numberOfColumns:5];
[matrix setCellSize:NSMakeSize(116, 96)];
[matrix setNeedsDisplay:YES];
[matrixContainerView addSubview:[matrix autorelease]];
[matrixContainerView setNeedsDisplay:YES];
The class MyCustomCell header looks like the following:
#interface MyCustomCell : NSCell {
MyModel * theObject;
}
-(MyModel *)theObject;
-(void)setTheObject:(MyModel *)newValue;
And the implementation file as follows (drawing simplified):
#implementation MyCustomCell
-(void)drawInteriorWithFrame:(NSRect)theFrame inView:(NSView *)theView {
...drawing code using MyModel e.g. [MyModel isValid] etc...
}
-(MyModel *)theObject {
return theObject;
}
-(void)setTheObject:(MyModel *)newValue {
[theObject autorelease];
theObject = [newValue retain];
}
#end
After some initialization and population of the array containing MyModel objects in the controller, I want to populate the NSMatrix with instances of the objects.
How do I do this?
I have tried adding just two objects from the array as follows (just as a test):
MyCustomCell * cellOne = (MyCustomCell *)[matrix cellAtRow:0 column:0];
[cell setTheObject:[myArrayOfObjects objectAtIndex:0]];
MyCustomCell * cellTwo = (MyCustomCell *)[matrix cellAtRow:0 column:1];
[cellTwo setTheObject:[myArrayOfObjects objectAtIndex:1]];
But this just creates the first object image. If the above had worked, it would been a straightforward task of enumerating through the array and adding the objects.
How do I go about adding the cells and passing the appropriate objects to those cells in order that they can be displayed correctly?
The Apple docs are sparse to say the least on NSMatrix as far as the programming guide goes. The information in there is very useful to me, but only after I have added the objects and got them displaying!
Update
If I do not add the two objects (as per my example above) the output is no different, i.e. a single representation of my custom cell is drawn to screen. This tells me that the single representation I see is being done at the initialization of the matrix and in fact I wasn't drawing anything to column 0 row 0 when in fact I thought I was. Which leaves me now more confused.
Might be that the matrix actually has the two cells but its frame is too small to display them?
After adding the cells try calling [matrix sizeToCells]
I am struggling trying to perform what I would think would be a relatively common task. I have an NSTableView that is bound to it's array via an NSArrayController. The array controller has it's content set to an NSMutableArray that contains one or more NSObject instances of a model class. What I don't know how to do is expose the model inside the NSCell subclass in a way that is bindings friendly.
For the purpose of illustration, we'll say that the object model is a person consisting of a first name, last name, age and gender. Thus the model would appear something like this:
#interface PersonModel : NSObject {
NSString * firstName;
NSString * lastName;
NSString * gender;
int * age;
}
Obviously the appropriate setters, getters init etc for the class.
In my controller class I define an NSTableView, NSMutableArray and an NSArrayController:
#interface ControllerClass : NSObject {
IBOutlet NSTableView * myTableView;
NSMutableArray * myPersonArray;
IBOutlet NSArrayController * myPersonArrayController;
}
Using Interface Builder I can easily bind the model to the appropriate columns:
myPersonArray --> myPersonArrayController --> table column binding
This works fine. So I remove the extra columns, leaving one column hidden that is bound to the NSArrayController (this creates and keeps the association between each row and the NSArrayController) so that I am down to one visible column in my NSTableView and one hidden column. I create an NSCell subclass and put the appropriate drawing method to create the cell. In my awakeFromNib I establish the custom NSCell subclass:
MyCustomCell * aCustomCell = [[[MyCustomCell alloc] init] autorelease];
[[myTableView tableColumnWithIdentifier:#"customCellColumn"]
setDataCell:aCustomCell];
This, too, works fine from a drawing perspective. I get my custom cell showing up in the column and it repeats for every managed object in my array controller. If I add an object or remove an object from the array controller the table updates accordingly.
However... I was under the impression that my PersonModel object would be available from within my NSCell subclass. But I don't know how to get to it. I don't want to set each NSCell using setters and getters because then I'm breaking the whole model concept by storing data in the NSCell instead of referencing it from the array controller.
And yes I do need to have a custom NSCell, so having multiple columns is not an option. Where to from here?
In addition to the Google and StackOverflow search, I've done the obligatory walk through on Apple's docs and don't seem to have found the answer. I have found a lot of references that beat around the bush but nothing involving an NSArrayController. The controller makes life very easy when binding to other elements of the model entity (such as a master/detail scenario). I have also found a lot of references (although no answers) when using Core Data, but Im not using Core Data.
As per the norm, I'm very grateful for any assistance that can be offered!
Finally figured this one out. Man that took some doing. So here is what I needed to do...
First of all I needed to create an array of my model's key values in my model object and return those key values in an NSDictionary from within the model.
Thus my model got two new methods as follows (based on my simplified example above):
+(NSArray *)personKeys
{
static NSArray * personKeys = nil;
if (personKeys == nil)
personKeys = [[NSArray alloc] initWithObjects:#"firstName", #"lastName", #"gender", #"age", nil];
return personKeys;
}
-(NSDictionary *)personDictionary
{
return [self dictionaryWithValuesForKeys:[[self class] personKeys]];
}
Once implemented, I assign my bound value in my table to
arrayController --> arrangeObjects --> personDictionary.
The last step is to reference the object in the NSCell drawWithFrame and use as needed as follows:
NSDictionary * thisCellObject = [self objectValue];
NSString * objectFirstName = [thisCellObject valueForkey:#"firstName"];
NSString * objectLastName = [thisCellObject valueForKey:#"lastName"];
And as I hoped, any update to the model object reflects in the custom NSCell.
There is another approach that does not require the dictionary. However, it does require the implementation of the - (id)copyWithZone:(NSZone *)zone method within your data class. For example:
- (id)copyWithZone:(NSZone *)zone {
Activity *copy = [[self class] allocWithZone: zone];
copy.activityDate = self.activityDate;
copy.sport = self.sport;
copy.sportIcon = self.sportIcon;
copy.laps = self.laps;
return copy; }
Now in IB, you point the Table Column's value at Array Controller --> arrangedObjects
The drawWithFrame method will now return your actual object from the objectValue.
Activity *rowActivity = [self objectValue];
Changing the class requires updating the copyWithZone method and then accessing the data directly in your drawWithFrame method.
I'm very grateful for this post, Hooligancat, because my project was stalled on this problem, and as hard as I looked at Tim Isted's identical solution at http://www.timisted.net/blog/archive/custom-cells-and-core-data/
I couldn't figure it out.
It was only when I read your excellent simplification of the problem and your version of the solution that the penny dropped - I'm very grateful!