Very odd behaviour with MultiDocumentPanel

Hey there, this is a really odd one, and I’m not entirely sure of the cause of it… I’ve pulled out a fair amount of hair already

Crux: Can we have ‘virtual void MultiDocumentPanel::documentClosing(Component* c);’ function called directly from closeDocument before the component is actually removed?

REASON:

Basically, I need to act based on a component closing in MultiDocumentPanel. We have the virtual ‘tryToCloseDocument()’, but no place to respond to the document actually being closed. I could just do my stuff before returning true, but I don’t really like that organisation.

So, the only place to respond is to notify my container from my Component’s destructor. A little messy, but theoretically doable.

The problem is, my response needs to do a ‘getNumDocuments()’. Looking at the MultiDocumentPanel.cpp, it’s apparent that the component is deleted before being removed from the document array, which is fine - getNumDocuments() (at the point of the component’s deletion) should include that Component. As long as I know that, that’s great.

Except that it’s not. For some reason, it’s not always the case, as can be proven with the debugger [I’m using VC++e2008].

[I’m using floating windows when I’m testing this, as they have the close button.]

Here’s the part of MultiDocumentPanel.cpp you can see it in…

264:       dw->setContentComponent (0, false);
265:       delete dw;
266:       break;
267:    }
268: }
269:
270: if (shouldDelete)
271: delete component;
272:
273: components.removeValue (component); 

Put a breakpoint on line 265, so it stops just before the floating window is deleted. If you hover over components, you’ll see numUsed showing the expected value. Step onwards, and at each line until the component is removed from the array, components shows this value. That’s what you expect.

Now put a breakpoint on any valid line from 266 to 273. Now components.numUsed shows that it has already been decremented - stepping past line 273 doesn’t change the value, the Component has magically already been removed. This is the state my destructor finds it in - the code all points to it still being there, but it has already been removed.

Why is this? I imagine it’s some kind of threading issue. I have a dual core Athlon64X2. I can’t really decide what to do to solve the problem, as I can’t guarantee what the value will be.

Thus, the question at the top.

What’s inside dw’s destructor ?

Does it have any mean to modify “components” ?
And what if you comment line 273, (to find out who has deleted the component ?)

What about a making a conditional breakpoint on components.numUsed to find out who decremented it ?

well, it looks like it’s down to the TopLevelWindow stuff. dw is a DocumentWindow subclass, so when it’s deleted it gets involved with TopLevelWindowManager’s timer - it plays with the order of the windows, resulting in a call to MultiDocumentPanel::updateOrder(), which is probably the cause (clears components, then adds them back based on the current child components - dw was one of them but is no more).

Well that’s that problem identified… any chance of getting that function added?

I was kind of expecting people to use tryToCloseDocument for doing their pre-shutdown activities, and using closeDocument just to actually get rid of it. Not sure I see the point in having another callback as well…?

To me it just doesn’t seem right to do something definitive in that function - there’s no guarantee that the document WILL be closed once it’s been called (after all, it is a public function so it could be called from anywhere).
The function description says that it’s a test to see if it’s safe for the document to close, and while it IS called by MultiDocumentPanel, someone might be using some code that employs this test for some other purpose.

It just seems like a safer logical model to have a callback saying “this component is actually closing now” - this would have the added bonus of being called regardless of the test being performed (e.g. closeDocument(c,false)).

That’s my opinion though, I’ll go with your suggestion :slight_smile:

well your document’s destructor gets called when it’s deleted, so you can do your shutdown things there too. You’re right that there are probably some cases where it’s useful to have this extra callback though.