From ce181bb0cd9855442f7a1db340e7e9d51f231e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Thu, 22 Jun 2006 00:22:23 +0000 Subject: [PATCH] * fixed memory leaks and display bugs * VectorPath objects were released one two many times by the Shape PathContainers (they don't acquire when a path is added, the Shape does that for them) the PathContainer of the Icon needs to release though, as it "owns" the paths * put the Selection class used by PathManipulator into the PathManipulator namespace, since the compiler seemed to use the wrong destructor (the one from the generic Selection class) * uses a better mechanism to track and render changed parts of icon git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17903 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/icon-o-matic/CanvasView.cpp | 37 +++++++- src/apps/icon-o-matic/CanvasView.h | 10 ++- src/apps/icon-o-matic/IconEditorApp.cpp | 4 + src/apps/icon-o-matic/MainWindow.cpp | 2 + src/apps/icon-o-matic/MainWindow.h | 3 + src/apps/icon-o-matic/document/Document.cpp | 1 + src/apps/icon-o-matic/document/Icon.cpp | 90 +++++++++++++++++-- src/apps/icon-o-matic/document/Icon.h | 35 +++++++- src/apps/icon-o-matic/shape/PathContainer.cpp | 8 +- src/apps/icon-o-matic/shape/PathContainer.h | 3 +- .../icon-o-matic/shape/PathManipulator.cpp | 2 +- src/apps/icon-o-matic/shape/PathManipulator.h | 2 +- src/apps/icon-o-matic/shape/Shape.cpp | 49 ++++++++-- src/apps/icon-o-matic/shape/Shape.h | 11 ++- .../icon-o-matic/shape/ShapeContainer.cpp | 4 +- 15 files changed, 231 insertions(+), 30 deletions(-) diff --git a/src/apps/icon-o-matic/CanvasView.cpp b/src/apps/icon-o-matic/CanvasView.cpp index ff50c913d9..18e7e59cf5 100644 --- a/src/apps/icon-o-matic/CanvasView.cpp +++ b/src/apps/icon-o-matic/CanvasView.cpp @@ -21,7 +21,9 @@ CanvasView::CanvasView(BRect frame) : StateView(frame, "canvas view", B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS), fBitmap(new BBitmap(BRect(0, 0, 63, 63), 0, B_RGB32)), + fIcon(NULL), fRenderer(new IconRenderer(fBitmap)), + fDirtyIconArea(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN), fCanvasOrigin(50.0, 50.0), fZoomLevel(8.0), @@ -37,6 +39,7 @@ CanvasView::CanvasView(BRect frame) // destructor CanvasView::~CanvasView() { + SetIcon(NULL); delete fRenderer; delete fBitmap; @@ -101,13 +104,40 @@ CanvasView::Draw(BRect updateRect) } } +// #pragma mark - + +// AreaInvalidated +void +CanvasView::AreaInvalidated(const BRect& area) +{ + if (fDirtyIconArea.Contains(area)) + return; + + fDirtyIconArea = fDirtyIconArea | area; + + BRect viewArea(area); + ConvertFromCanvas(&viewArea); + Invalidate(viewArea); +} + + // #pragma mark - // SetIcon void CanvasView::SetIcon(Icon* icon) { + if (fIcon == icon) + return; + + if (fIcon) + fIcon->RemoveListener(this); + + fIcon = icon; fRenderer->SetIcon(icon); + + if (fIcon) + fIcon->AddListener(this); } // ConvertFromCanvas @@ -234,9 +264,10 @@ CanvasView::_FreeBackBitmap() void CanvasView::_DrawInto(BView* view, BRect updateRect) { - // TODO: don't render here, use some - // listener technique on the shapes instead... - fRenderer->Render(); + if (fDirtyIconArea.IsValid()) { + fRenderer->Render(fDirtyIconArea); + fDirtyIconArea.Set(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN); + } // icon BRect canvas(_CanvasRect()); diff --git a/src/apps/icon-o-matic/CanvasView.h b/src/apps/icon-o-matic/CanvasView.h index 993231ff7f..3370163859 100644 --- a/src/apps/icon-o-matic/CanvasView.h +++ b/src/apps/icon-o-matic/CanvasView.h @@ -9,13 +9,14 @@ #ifndef CANVAS_VIEW_H #define CANVAS_VIEW_H +#include "Icon.h" #include "StateView.h" class BBitmap; -class Icon; class IconRenderer; -class CanvasView : public StateView { +class CanvasView : public StateView, + public IconListener { public: CanvasView(BRect frame); virtual ~CanvasView(); @@ -25,6 +26,9 @@ class CanvasView : public StateView { virtual void FrameResized(float width, float height); virtual void Draw(BRect updateRect); + // IconListener interface + virtual void AreaInvalidated(const BRect& area); + // CanvasView void SetIcon(Icon* icon); @@ -52,7 +56,9 @@ class CanvasView : public StateView { private: BBitmap* fBitmap; + Icon* fIcon; IconRenderer* fRenderer; + BRect fDirtyIconArea; BPoint fCanvasOrigin; float fZoomLevel; diff --git a/src/apps/icon-o-matic/IconEditorApp.cpp b/src/apps/icon-o-matic/IconEditorApp.cpp index 9c0bec89a5..548f8049a3 100644 --- a/src/apps/icon-o-matic/IconEditorApp.cpp +++ b/src/apps/icon-o-matic/IconEditorApp.cpp @@ -28,6 +28,10 @@ IconEditorApp::IconEditorApp() // destructor IconEditorApp::~IconEditorApp() { + // NOTE: it is important that the GUI has been deleted + // at this point, so that all the listener/observer + // stuff is properly detached + delete fDocument; } // #pragma mark - diff --git a/src/apps/icon-o-matic/MainWindow.cpp b/src/apps/icon-o-matic/MainWindow.cpp index 6365b941bb..a65dd3f56e 100644 --- a/src/apps/icon-o-matic/MainWindow.cpp +++ b/src/apps/icon-o-matic/MainWindow.cpp @@ -44,6 +44,7 @@ MainWindow::MainWindow(IconEditorApp* app, Document* document) // destructor MainWindow::~MainWindow() { + delete fState; } // #pragma mark - @@ -139,6 +140,7 @@ MainWindow::_Init() PathManipulator* pathManipulator = new PathManipulator(path); state->AddManipulator(pathManipulator); + fState = state; // ---- } diff --git a/src/apps/icon-o-matic/MainWindow.h b/src/apps/icon-o-matic/MainWindow.h index 3977787b81..6b43951e7c 100644 --- a/src/apps/icon-o-matic/MainWindow.h +++ b/src/apps/icon-o-matic/MainWindow.h @@ -14,6 +14,7 @@ class CanvasView; class Document; class IconEditorApp; +class ViewState; class MainWindow : public BWindow { public: @@ -33,6 +34,8 @@ class MainWindow : public BWindow { Document* fDocument; CanvasView* fCanvasView; + // TODO: for testing only... + ViewState* fState; }; #endif // MAIN_WINDOW_H diff --git a/src/apps/icon-o-matic/document/Document.cpp b/src/apps/icon-o-matic/document/Document.cpp index 52ae5ea5bc..5e38fff098 100644 --- a/src/apps/icon-o-matic/document/Document.cpp +++ b/src/apps/icon-o-matic/document/Document.cpp @@ -33,6 +33,7 @@ Document::Document(const char* name) // destructor Document::~Document() { + delete fIcon; delete fCommandStack; delete fSelection; delete fRef; diff --git a/src/apps/icon-o-matic/document/Icon.cpp b/src/apps/icon-o-matic/document/Icon.cpp index d9c25cb1dc..6d17863405 100644 --- a/src/apps/icon-o-matic/document/Icon.cpp +++ b/src/apps/icon-o-matic/document/Icon.cpp @@ -11,21 +11,101 @@ #include #include "PathContainer.h" -#include "ShapeContainer.h" +#include "Shape.h" using std::nothrow; // constructor -Icon::Icon() - : fPaths(new (nothrow) PathContainer()), - fShapes(new (nothrow) ShapeContainer()) +IconListener::IconListener() { } +// destructor +IconListener::~IconListener() +{ +} + +// #pragma mark - + +// constructor +Icon::Icon() + : fPaths(new (nothrow) PathContainer(true)), + fShapes(new (nothrow) ShapeContainer()), + fListeners(2) +{ + if (fShapes) + fShapes->AddListener(this); +} // destructor Icon::~Icon() { - delete fShapes; + if (fShapes) { + fShapes->MakeEmpty(); + fShapes->RemoveListener(this); + delete fShapes; + } delete fPaths; } + +// ShapeAdded +void +Icon::ShapeAdded(Shape* shape) +{ + shape->AddObserver(this); + _NotifyAreaInvalidated(shape->Bounds()); +} + +// ShapeRemoved +void +Icon::ShapeRemoved(Shape* shape) +{ + shape->RemoveObserver(this); + _NotifyAreaInvalidated(shape->Bounds()); +} + +// ObjectChanged +void +Icon::ObjectChanged(const Observable* object) +{ + const Shape* shape = dynamic_cast(object); + if (shape) { + BRect area = shape->LastBounds(); + area = area | shape->Bounds(true); + area.InsetBy(-1, -1); + _NotifyAreaInvalidated(area); + } +} + +// AddListener +bool +Icon::AddListener(IconListener* listener) +{ + if (listener && !fListeners.HasItem((void*)listener)) + return fListeners.AddItem((void*)listener); + return false; +} + +// RemoveListener +bool +Icon::RemoveListener(IconListener* listener) +{ + return fListeners.RemoveItem((void*)listener); +} + +// #pragma mark - + +// _NotifyShapeRemoved +void +Icon::_NotifyAreaInvalidated(const BRect& area) const +{ + BList listeners(fListeners); + int32 count = listeners.CountItems(); + for (int32 i = 0; i < count; i++) { + IconListener* listener + = (IconListener*)listeners.ItemAtFast(i); + listener->AreaInvalidated(area); + } +} + + diff --git a/src/apps/icon-o-matic/document/Icon.h b/src/apps/icon-o-matic/document/Icon.h index 09e1fa79fa..ae3fc20438 100644 --- a/src/apps/icon-o-matic/document/Icon.h +++ b/src/apps/icon-o-matic/document/Icon.h @@ -9,22 +9,51 @@ #ifndef ICON_H #define ICON_H -class PathContainer; -class ShapeContainer; +#include -class Icon { +#include "Observer.h" +#include "ShapeContainer.h" + +class PathContainer; + +class IconListener { + public: + IconListener(); + virtual ~IconListener(); + + virtual void AreaInvalidated(const BRect& area) = 0; +}; + +class Icon : public ShapeContainerListener, + public Observer { public: Icon(); virtual ~Icon(); + // ShapeContainerListener interface + virtual void ShapeAdded(Shape* shape); + virtual void ShapeRemoved(Shape* shape); + + // Observer interface + virtual void ObjectChanged(const Observable* object); + + // Icon PathContainer* Paths() const { return fPaths; } ShapeContainer* Shapes() const { return fShapes; } + bool AddListener(IconListener* listener); + bool RemoveListener(IconListener* listener); + private: + void _NotifyAreaInvalidated( + const BRect& area) const; + PathContainer* fPaths; ShapeContainer* fShapes; + + BList fListeners; }; #endif // ICON_H diff --git a/src/apps/icon-o-matic/shape/PathContainer.cpp b/src/apps/icon-o-matic/shape/PathContainer.cpp index 1b566679d1..c5ae4934c3 100644 --- a/src/apps/icon-o-matic/shape/PathContainer.cpp +++ b/src/apps/icon-o-matic/shape/PathContainer.cpp @@ -28,9 +28,10 @@ PathContainerListener::~PathContainerListener() // constructor -PathContainer::PathContainer() +PathContainer::PathContainer(bool ownsPaths) : fPaths(16), - fListeners(2) + fListeners(2), + fOwnsPaths(ownsPaths) { } @@ -156,7 +157,8 @@ PathContainer::_MakeEmpty() for (int32 i = 0; i < count; i++) { VectorPath* path = PathAtFast(i); _NotifyPathRemoved(path); - path->Release(); + if (fOwnsPaths) + path->Release(); } fPaths.MakeEmpty(); } diff --git a/src/apps/icon-o-matic/shape/PathContainer.h b/src/apps/icon-o-matic/shape/PathContainer.h index 01927aef41..fa400bab5d 100644 --- a/src/apps/icon-o-matic/shape/PathContainer.h +++ b/src/apps/icon-o-matic/shape/PathContainer.h @@ -24,7 +24,7 @@ class PathContainerListener { class PathContainer { public: - PathContainer(); + PathContainer(bool ownsPaths); virtual ~PathContainer(); bool AddPath(VectorPath* path); @@ -50,6 +50,7 @@ class PathContainer { BList fPaths; BList fListeners; + bool fOwnsPaths; }; #endif // PATH_CONTAINER_H diff --git a/src/apps/icon-o-matic/shape/PathManipulator.cpp b/src/apps/icon-o-matic/shape/PathManipulator.cpp index bbedd03e0b..197c0f9522 100644 --- a/src/apps/icon-o-matic/shape/PathManipulator.cpp +++ b/src/apps/icon-o-matic/shape/PathManipulator.cpp @@ -109,7 +109,7 @@ string_for_mode(uint32 mode) return ""; } -class Selection : protected BList +class PathManipulator::Selection : protected BList { public: inline Selection(int32 count = 20) diff --git a/src/apps/icon-o-matic/shape/PathManipulator.h b/src/apps/icon-o-matic/shape/PathManipulator.h index b33705183f..a0674a0d12 100644 --- a/src/apps/icon-o-matic/shape/PathManipulator.h +++ b/src/apps/icon-o-matic/shape/PathManipulator.h @@ -16,7 +16,6 @@ class CanvasView; class ChangePointCommand; class UndoStack; class InsertPointCommand; -class Selection; class VectorPath; //class PathSelection { @@ -146,6 +145,7 @@ class PathManipulator : public Manipulator { InsertPointCommand* fInsertPointCommand; AddPointCommand* fAddPointCommand; + class Selection; Selection* fSelection; Selection* fOldSelection; diff --git a/src/apps/icon-o-matic/shape/Shape.cpp b/src/apps/icon-o-matic/shape/Shape.cpp index dee99d857e..ce2f19e514 100644 --- a/src/apps/icon-o-matic/shape/Shape.cpp +++ b/src/apps/icon-o-matic/shape/Shape.cpp @@ -2,6 +2,9 @@ #include #include +#include + +#include "agg_bounding_rect.h" #include "Style.h" #include "VectorPath.h" @@ -13,10 +16,14 @@ Shape::Shape(::Style* style) : Observable(), Referenceable(), PathContainerListener(), - fPaths(new (nothrow) PathContainer()), + + fPaths(new (nothrow) PathContainer(false)), fStyle(style), + fPathSource(fPaths), - fTransformers(4) + fTransformers(4), + + fLastBounds(0, 0, -1, -1) { if (fPaths) fPaths->AddListener(this); @@ -45,17 +52,31 @@ void Shape::PathAdded(VectorPath* path) { path->Acquire(); + path->AddObserver(this); + Notify(); } // PathRemoved void Shape::PathRemoved(VectorPath* path) { + path->RemoveObserver(this); + Notify(); path->Release(); } // #pragma mark - +// ObjectChanged +void +Shape::ObjectChanged(const Observable* object) +{ + // simply pass on the event for now + Notify(); +} + +// #pragma mark - + // InitCheck status_t Shape::InitCheck() const @@ -83,14 +104,26 @@ Shape::SetStyle(::Style* style) // Bounds BRect -Shape::Bounds() const +Shape::Bounds(bool updateLast) const { - // TODO: incorrect - use VertexSource instead!! - BRect bounds(LONG_MAX, LONG_MAX, LONG_MIN, LONG_MIN); + // TODO: what about sub-paths?!? + // the problem is that the path ids are + // nowhere stored while converting VectorPath + // to agg::path_storage, but it is also unclear + // if those would mean anything later on in + // the Transformer pipeline + uint32 pathID[1]; + pathID[0] = 0; + double left, top, right, bottom; - int32 count = fPaths->CountPaths(); - for (int32 i = 0; i < count; i++) - bounds = bounds | fPaths->PathAtFast(i)->Bounds(); + ::VertexSource& source = const_cast(this)->VertexSource(); + agg::bounding_rect(source, pathID, 0, 1, + &left, &top, &right, &bottom); + + BRect bounds(left, top, right, bottom); + + if (updateLast) + fLastBounds = bounds; return bounds; } diff --git a/src/apps/icon-o-matic/shape/Shape.h b/src/apps/icon-o-matic/shape/Shape.h index b17a3a1193..e0409ad3c1 100644 --- a/src/apps/icon-o-matic/shape/Shape.h +++ b/src/apps/icon-o-matic/shape/Shape.h @@ -5,6 +5,7 @@ #include #include "Observable.h" +#include "Observer.h" #include "PathContainer.h" #include "PathSource.h" #include "Referenceable.h" @@ -12,6 +13,7 @@ class Style; class Shape : public Observable, + public Observer, // observing all the paths public Referenceable, public PathContainerListener { public: @@ -22,6 +24,9 @@ class Shape : public Observable, virtual void PathAdded(VectorPath* path); virtual void PathRemoved(VectorPath* path); + // Observer interface + virtual void ObjectChanged(const Observable* object); + // Shape status_t InitCheck() const; @@ -32,7 +37,9 @@ class Shape : public Observable, inline ::Style* Style() const { return fStyle; } - BRect Bounds() const; + inline BRect LastBounds() const + { return fLastBounds; } + BRect Bounds(bool updateLast = false) const; ::VertexSource& VertexSource(); bool AppendTransformer( @@ -44,6 +51,8 @@ class Shape : public Observable, PathSource fPathSource; BList fTransformers; + + mutable BRect fLastBounds; }; #endif // SHAPE_H diff --git a/src/apps/icon-o-matic/shape/ShapeContainer.cpp b/src/apps/icon-o-matic/shape/ShapeContainer.cpp index 03270af1ee..de939ad6ab 100644 --- a/src/apps/icon-o-matic/shape/ShapeContainer.cpp +++ b/src/apps/icon-o-matic/shape/ShapeContainer.cpp @@ -135,7 +135,7 @@ bool ShapeContainer::AddListener(ShapeContainerListener* listener) { if (listener && !fListeners.HasItem((void*)listener)) - return fListeners.AddItem(listener); + return fListeners.AddItem((void*)listener); return false; } @@ -143,7 +143,7 @@ ShapeContainer::AddListener(ShapeContainerListener* listener) bool ShapeContainer::RemoveListener(ShapeContainerListener* listener) { - return fListeners.RemoveItem(listener); + return fListeners.RemoveItem((void*)listener); } // #pragma mark -