Fix issue #310 - crash on export sprite sheet

The timeline wasn't being added as a Document observer, so it wasn't
getting notifications about layers modifications. In this way the timeline
wasn't able to update its m_layers[] internal field and was accessing to
removed layers/invalid memory.
This commit is contained in:
David Capello 2014-03-29 16:31:39 -03:00
parent 03020d7bcf
commit 395be62b03
7 changed files with 83 additions and 63 deletions

View File

@ -632,7 +632,7 @@ void DocumentApi::removeLayer(Layer* layer)
DocumentEvent ev(m_document); DocumentEvent ev(m_document);
ev.sprite(layer->getSprite()); ev.sprite(layer->getSprite());
ev.layer(layer); ev.layer(layer);
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onRemoveLayer, ev); m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onBeforeRemoveLayer, ev);
// Add undoers. // Add undoers.
if (undoEnabled()) if (undoEnabled())
@ -640,6 +640,9 @@ void DocumentApi::removeLayer(Layer* layer)
// Do the action. // Do the action.
layer->getParent()->removeLayer(layer); layer->getParent()->removeLayer(layer);
m_document->notifyObservers<DocumentEvent&>(&DocumentObserver::onAfterRemoveLayer, ev);
delete layer; delete layer;
} }

View File

@ -41,7 +41,8 @@ namespace app {
virtual void onRemoveSprite(DocumentEvent& ev) { } virtual void onRemoveSprite(DocumentEvent& ev) { }
virtual void onRemoveLayer(DocumentEvent& ev) { } virtual void onBeforeRemoveLayer(DocumentEvent& ev) { }
virtual void onAfterRemoveLayer(DocumentEvent& ev) { }
// Called when a frame is removed. It's called after the frame was // Called when a frame is removed. It's called after the frame was
// removed, and the sprite's total number of frames is modified. // removed, and the sprite's total number of frames is modified.

View File

@ -218,7 +218,7 @@ void DocumentView::onAddLayer(DocumentEvent& ev)
} }
} }
void DocumentView::onRemoveLayer(DocumentEvent& ev) void DocumentView::onBeforeRemoveLayer(DocumentEvent& ev)
{ {
Sprite* sprite = ev.sprite(); Sprite* sprite = ev.sprite();
Layer* layer = ev.layer(); Layer* layer = ev.layer();

View File

@ -64,7 +64,7 @@ namespace app {
void onSpritePixelsModified(DocumentEvent& ev) OVERRIDE; void onSpritePixelsModified(DocumentEvent& ev) OVERRIDE;
void onLayerMergedDown(DocumentEvent& ev) OVERRIDE; void onLayerMergedDown(DocumentEvent& ev) OVERRIDE;
void onAddLayer(DocumentEvent& ev) OVERRIDE; void onAddLayer(DocumentEvent& ev) OVERRIDE;
void onRemoveLayer(DocumentEvent& ev) OVERRIDE; void onBeforeRemoveLayer(DocumentEvent& ev) OVERRIDE;
void onAddFrame(DocumentEvent& ev) OVERRIDE; void onAddFrame(DocumentEvent& ev) OVERRIDE;
void onRemoveFrame(DocumentEvent& ev) OVERRIDE; void onRemoveFrame(DocumentEvent& ev) OVERRIDE;
void onTotalFramesChanged(DocumentEvent& ev) OVERRIDE; void onTotalFramesChanged(DocumentEvent& ev) OVERRIDE;

View File

@ -180,6 +180,8 @@ void Timeline::updateUsingEditor(Editor* editor)
DocumentView* view = m_editor->getDocumentView(); DocumentView* view = m_editor->getDocumentView();
view->getDocumentLocation(&location); view->getDocumentLocation(&location);
location.document()->addObserver(this);
// If we are already in the same position as the "editor", we don't // If we are already in the same position as the "editor", we don't
// need to update the at all timeline. // need to update the at all timeline.
if (m_document == location.document() && if (m_document == location.document() &&
@ -859,11 +861,13 @@ void Timeline::onPreferredSize(PreferredSizeEvent& ev)
void Timeline::onPaint(ui::PaintEvent& ev) void Timeline::onPaint(ui::PaintEvent& ev)
{ {
Graphics* g = ev.getGraphics(); Graphics* g = ev.getGraphics();
bool noDoc = (m_document == NULL);
if (noDoc)
goto paintNoDoc;
if (!m_document) { try {
drawPart(g, getClientBounds(), NULL, m_timelinePaddingStyle); // Lock the sprite to read/render it.
return; const DocumentReader documentReader(m_document);
}
int layer, first_layer, last_layer; int layer, first_layer, last_layer;
FrameNumber frame, first_frame, last_frame; FrameNumber frame, first_frame, last_frame;
@ -925,6 +929,14 @@ void Timeline::onPaint(ui::PaintEvent& ev)
} }
drawPaddings(g); drawPaddings(g);
}
catch (const LockedDocumentException&) {
noDoc = true;
}
paintNoDoc:;
if (noDoc)
drawPart(g, getClientBounds(), NULL, m_timelinePaddingStyle);
} }
void Timeline::onCommandAfterExecution(Context* context) void Timeline::onCommandAfterExecution(Context* context)
@ -954,7 +966,7 @@ void Timeline::onAddLayer(DocumentEvent& ev)
invalidate(); invalidate();
} }
void Timeline::onRemoveLayer(DocumentEvent& ev) void Timeline::onAfterRemoveLayer(DocumentEvent& ev)
{ {
Sprite* sprite = ev.sprite(); Sprite* sprite = ev.sprite();
Layer* layer = ev.layer(); Layer* layer = ev.layer();

View File

@ -114,7 +114,7 @@ namespace app {
// DocumentObserver impl. // DocumentObserver impl.
void onAddLayer(DocumentEvent& ev) OVERRIDE; void onAddLayer(DocumentEvent& ev) OVERRIDE;
void onRemoveLayer(DocumentEvent& ev) OVERRIDE; void onAfterRemoveLayer(DocumentEvent& ev) OVERRIDE;
void onAddFrame(DocumentEvent& ev) OVERRIDE; void onAddFrame(DocumentEvent& ev) OVERRIDE;
void onRemoveFrame(DocumentEvent& ev) OVERRIDE; void onRemoveFrame(DocumentEvent& ev) OVERRIDE;

View File

@ -39,6 +39,7 @@ public:
// collection and will be destroyed calling the T::dispose() member // collection and will be destroyed calling the T::dispose() member
// function. // function.
void addObserver(observer_type* observer) { void addObserver(observer_type* observer) {
ASSERT(std::find(m_observers.begin(), m_observers.end(), observer) == m_observers.end() && "You've tried to add an observer that already is in the collection");
m_observers.push_back(observer); m_observers.push_back(observer);
} }
@ -48,6 +49,9 @@ public:
iterator it = std::find(m_observers.begin(), m_observers.end(), observer); iterator it = std::find(m_observers.begin(), m_observers.end(), observer);
if (it != end()) if (it != end())
m_observers.erase(it); m_observers.erase(it);
else {
ASSERT(false && "You've tried to remove an observer that isn't in the collection");
}
} }
void notifyObservers(void (observer_type::*method)()) { void notifyObservers(void (observer_type::*method)()) {