##// END OF EJS Templates
Michal Klocek -
r391:7d2e92012cfd
parent child
Show More
@@ -28,6 +28,7 int main(int argc, char *argv[])
28
28
29 chartView->setViewport( new QGLWidget() );
29 chartView->setViewport( new QGLWidget() );
30 chartView->setRenderHint(QPainter::Antialiasing);
30 chartView->setRenderHint(QPainter::Antialiasing);
31 chartView->setAnimationOptions(QChart::AllAnimations);
31 chartView->setChartTitle("This is wave generator buahha.");
32 chartView->setChartTitle("This is wave generator buahha.");
32 chartView->addSeries(series0);
33 chartView->addSeries(series0);
33 chartView->addSeries(series1);
34 chartView->addSeries(series1);
@@ -9,6 +9,7 int main(int argc, char *argv[])
9 ChartView chartView(&window);
9 ChartView chartView(&window);
10 chartView.setRenderHint(QPainter::Antialiasing);
10 chartView.setRenderHint(QPainter::Antialiasing);
11 chartView.setChartTitle("Three random line charts");
11 chartView.setChartTitle("Three random line charts");
12 chartView.setAnimationOptions(QChart::AllAnimations);
12 window.setCentralWidget(&chartView);
13 window.setCentralWidget(&chartView);
13 window.resize(400, 300);
14 window.resize(400, 300);
14 window.show();
15 window.show();
@@ -1,78 +1,77
1 #include "linechartanimationitem_p.h"
1 #include "linechartanimationitem_p.h"
2 #include "linechartitem_p.h"
2 #include "linechartitem_p.h"
3 #include <QPropertyAnimation>
3 #include <QPropertyAnimation>
4 #include <QTimer>
5
6 Q_DECLARE_METATYPE(QVector<QPointF>)
4
7
5 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8 QTCOMMERCIALCHART_BEGIN_NAMESPACE
6
9
7 const static int duration = 500;
10 const static int duration = 500;
8
11
9
10 LineChartAnimationItem::LineChartAnimationItem(ChartPresenter* presenter, QLineSeries* series,QGraphicsItem *parent):
12 LineChartAnimationItem::LineChartAnimationItem(ChartPresenter* presenter, QLineSeries* series,QGraphicsItem *parent):
11 LineChartItem(presenter,series,parent)
13 LineChartItem(presenter,series,parent),
14 m_animation(new LineChartAnimatator(this,this)),
15 m_dirty(false)
12 {
16 {
13
14 }
17 }
15
18
16 LineChartAnimationItem::~LineChartAnimationItem()
19 LineChartAnimationItem::~LineChartAnimationItem()
17 {
20 {
18 }
21 }
19
22
20 void LineChartAnimationItem::addPoints(const QVector<QPointF>& points)
23 void LineChartAnimationItem::updateItem(QVector<QPointF>& oldPoints,QVector<QPointF>& newPoints)
21 {
24 {
22 QVector<qreal> vector0 = vector1;
25 if(newPoints.count()==0) return;
23 calculateLayout(vector1);
26 oldPoints.resize(newPoints.size());
24 if(vector1.count()==0) return;
25 vector0.resize(vector1.size());
26
27
28 LineChartAnimatator *animation = new LineChartAnimatator(this,this);
29 animation->setDuration(duration);
30 animation->setEasingCurve(QEasingCurve::InOutBack);
31 animation->setKeyValueAt(0.0, qVariantFromValue(vector0));
32 animation->setKeyValueAt(1.0, qVariantFromValue(vector1));
33 animation->start(QAbstractAnimation::DeleteWhenStopped);
34
35 QPropertyAnimation *animation = new QPropertyAnimation(this, "a_addPoints", parent());
36 animation->setDuration(duration);
37 //animation->setEasingCurve(QEasingCurve::InOutBack);
38 animation->setKeyValueAt(0.0, 0);
39 animation->setKeyValueAt(1.0, m_data.size());
40 animation->start(QAbstractAnimation::DeleteWhenStopped);
41 }
42
27
43 void LineChartAnimationItem::replacePoint(int index,const QPointF& point)
28 if(m_animation->state()!=QAbstractAnimation::Stopped){
44 {
29 m_animation->stop();
45 AnimationHelper* helper = new AnimationHelper(this,index);
30 }
46 QPropertyAnimation *animation = new QPropertyAnimation(helper, "point", parent());
31
47 animation->setDuration(duration);
32 m_animation->setDuration(duration);
48 //animation->setEasingCurve(QEasingCurve::InOutBack);
33 m_animation->setEasingCurve(QEasingCurve::InOutBack);
49 animation->setKeyValueAt(0.0, points().value(index));
34 m_animation->setKeyValueAt(0.0, qVariantFromValue(oldPoints));
50 animation->setKeyValueAt(1.0, point);
35 m_animation->setKeyValueAt(1.0, qVariantFromValue(newPoints));
51 animation->start(QAbstractAnimation::DeleteWhenStopped);
36 QTimer::singleShot(0,m_animation,SLOT(start()));
37
38 oldPoints = newPoints;
39 m_points = newPoints;
40 m_dirty=false;
52 }
41 }
53
42
54 void LineChartAnimationItem::aw_addPoints(int points)
43 void LineChartAnimationItem::updateItem(QVector<QPointF>& oldPoints,int index,QPointF& newPoint)
55 {
44 {
56 int index = count();
45 if(m_animation->state()!=QAbstractAnimation::Stopped){
57 for(int i = index;i< points ;i++){
46 m_animation->stop();
58 LineChartItem::addPoint(m_data.at(i));
47 m_dirty=true;
59 }
48 }
60 updateGeometry();
49
61 update();
50 if(m_dirty){
51 m_points=oldPoints;
52 m_dirty=false;
53 }
54
55 oldPoints.replace(index,newPoint);
56
57 m_animation->setDuration(duration);
58 m_animation->setEasingCurve(QEasingCurve::InOutBack);
59 m_animation->setKeyValueAt(0.0, qVariantFromValue(m_points));
60 m_animation->setKeyValueAt(1.0, qVariantFromValue(oldPoints));
61 QTimer::singleShot(0,this,SLOT(startAnimation()));
62
62 }
63 }
63
64
64 void LineChartAnimationItem::aw_setPoint(int index,const QPointF& point)
65 void LineChartAnimationItem::startAnimation()
65 {
66 {
66 LineChartItem::replacePoint(index,point);
67 m_dirty=true;
67 updateGeometry();
68 m_animation->start();
68 update();
69 }
69 }
70
70
71 LineChartAnimatator::LineChartAnimatator(LineChartItem *item, int index , QObject *parent = 0 ):QVariantAnimation(parent),
71 LineChartAnimatator::LineChartAnimatator(LineChartAnimationItem *item , QObject *parent):QVariantAnimation(parent),
72 m_item(item),
72 m_item(item)
73 m_index(index)
74 {
73 {
75 };
74 }
76
75
77 LineChartAnimatator::~LineChartAnimatator()
76 LineChartAnimatator::~LineChartAnimatator()
78 {
77 {
@@ -80,26 +79,25 LineChartAnimatator::~LineChartAnimatator()
80
79
81 QVariant LineChartAnimatator::interpolated(const QVariant &start, const QVariant & end, qreal progress ) const
80 QVariant LineChartAnimatator::interpolated(const QVariant &start, const QVariant & end, qreal progress ) const
82 {
81 {
83 QVector<qreal> startVector = qVariantValue<QVector<qreal> >(start);
82 QVector<QPointF> startVector = qVariantValue<QVector<QPointF> >(start);
84 QVector<qreal> endVecotr = qVariantValue<QVector<qreal> >(end);
83 QVector<QPointF> endVecotr = qVariantValue<QVector<QPointF> >(end);
85 QVector<qreal> result;
84 QVector<QPointF> result;
86 Q_ASSERT(startVector.count() == endVecotr.count());
85 Q_ASSERT(startVector.count() == endVecotr.count());
87
86
88 for(int i =0 ;i< startVector.count();i++){
87 for(int i =0 ;i< startVector.count();i++){
89 qreal value = startVector[i] + ((endVecotr[i]- startVector[i]) * progress);//qBound(0.0, progress, 1.0));
88 qreal x = startVector[i].x() + ((endVecotr[i].x()- startVector[i].x()) * progress);//qBound(0.0, progress, 1.0));
90 result << value;
89 qreal y = startVector[i].y() + ((endVecotr[i].y()- startVector[i].y()) * progress);//qBound(0.0, progress, 1.0));
90 result << QPointF(x,y);
91 }
91 }
92 return qVariantFromValue(result);
92 return qVariantFromValue(result);
93 }
93 }
94
94
95
96 void LineChartAnimatator::updateCurrentValue (const QVariant & value )
95 void LineChartAnimatator::updateCurrentValue (const QVariant & value )
97 {
96 {
98 QVector<qreal> vector = qVariantValue<QVector<qreal> >(value);
97 QVector<QPointF> vector = qVariantValue<QVector<QPointF> >(value);
99 m_axis->applyLayout(vector);
98 m_item->applyGeometry(vector);
100 }
99 }
101
100
102
103 #include "moc_linechartanimationitem_p.cpp"
101 #include "moc_linechartanimationitem_p.cpp"
104
102
105 QTCOMMERCIALCHART_END_NAMESPACE
103 QTCOMMERCIALCHART_END_NAMESPACE
@@ -4,37 +4,45
4 #include "qchartglobal.h"
4 #include "qchartglobal.h"
5 #include "linechartitem_p.h"
5 #include "linechartitem_p.h"
6 #include "domain_p.h"
6 #include "domain_p.h"
7 #include <QVariantAnimation>
7
8
8 QTCOMMERCIALCHART_BEGIN_NAMESPACE
9 QTCOMMERCIALCHART_BEGIN_NAMESPACE
9
10
10 class LineChartItem;
11 class LineChartAnimatator;
11
12
12 class LineChartAnimationItem : public LineChartItem {
13 class LineChartAnimationItem : public LineChartItem {
13
14
15 Q_OBJECT
16
14 public:
17 public:
15 LineChartAnimationItem(ChartPresenter* presenter, QLineSeries *series, QGraphicsItem *parent = 0);
18 LineChartAnimationItem(ChartPresenter* presenter, QLineSeries *series, QGraphicsItem *parent = 0);
16 virtual ~LineChartAnimationItem();
19 virtual ~LineChartAnimationItem();
17
20
18 void addPoints(const QVector<QPointF>& points);
21 protected:
19 void replacePoint(int index,const QPointF& point);
22 virtual void updateItem(QVector<QPointF>& oldPoints,QVector<QPointF>& newPoints);
23 virtual void updateItem(QVector<QPointF>& oldPoints,int index,QPointF& newPoint);
24
25 private slots:
26 void startAnimation();
20
27
21 private:
28 private:
22 QVector<QPointF> m_data;
29 LineChartAnimatator *m_animation;
23 Domain m_domain;
30 QVector<QPointF> m_points;
24 int m_addPoints;
31 bool m_dirty;
25 QPointF m_setPoint;
26 int m_setPoint_index;
27 };
32 };
28
33
29 class LineChartAnimatator: public QVariantAnimation
34 class LineChartAnimatator: public QVariantAnimation
30 {
35 {
31
32 public:
36 public:
33 LineChartAnimatator(LineChartItem *item, int index , QObject *parent = 0 ):m_item(item),m_index(index){};
37 LineChartAnimatator(LineChartAnimationItem *item, QObject *parent = 0 );
38 ~LineChartAnimatator();
34
39
35 QPointF m_point;
40 protected:
41 QVariant interpolated(const QVariant &start, const QVariant & end, qreal progress ) const;
42 void updateCurrentValue (const QVariant & value );
43
44 private:
36 LineChartAnimationItem* m_item;
45 LineChartAnimationItem* m_item;
37 int m_index;
38 };
46 };
39
47
40 QTCOMMERCIALCHART_END_NAMESPACE
48 QTCOMMERCIALCHART_END_NAMESPACE
@@ -6,12 +6,14
6
6
7 QTCOMMERCIALCHART_BEGIN_NAMESPACE
7 QTCOMMERCIALCHART_BEGIN_NAMESPACE
8
8
9 //TODO: optimazie : remove points which are not visible
9 //TODO: optimize : remove points which are not visible
10
10
11 LineChartItem::LineChartItem(ChartPresenter* presenter, QLineSeries* series,QGraphicsItem *parent):ChartItem(parent),
11 LineChartItem::LineChartItem(ChartPresenter* presenter, QLineSeries* series,QGraphicsItem *parent):ChartItem(parent),
12 m_presenter(presenter),
12 m_presenter(presenter),
13 m_series(series)
13 m_series(series),
14 m_items(this)
14 {
15 {
16 //m_items.setZValue(ChartPresenter::LineChartZValue);
15 setZValue(ChartPresenter::LineChartZValue);
17 setZValue(ChartPresenter::LineChartZValue);
16 }
18 }
17
19
@@ -28,7 +30,7 QPainterPath LineChartItem::shape() const
28 void LineChartItem::createPoints(int count)
30 void LineChartItem::createPoints(int count)
29 {
31 {
30 for (int i = 0; i < count; ++i) {
32 for (int i = 0; i < count; ++i) {
31 QGraphicsRectItem* item = new QGraphicsRectItem(0,0,3,3,this);
33 QGraphicsRectItem* item = new QGraphicsRectItem(0,0,3,3);
32 m_items.addToGroup(item);
34 m_items.addToGroup(item);
33 }
35 }
34 }
36 }
@@ -42,18 +44,7 void LineChartItem::clearPoints(int count)
42 }
44 }
43 }
45 }
44
46
45 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
47 QPointF LineChartItem::calculateGeometryPoint(int index) const
46 {
47 Q_UNUSED(widget);
48 Q_UNUSED(option);
49 painter->save();
50 painter->setPen(m_pen);
51 painter->setClipRect(m_clipRect);
52 painter->drawPath(m_path);
53 painter->restore();
54 }
55
56 QPointF LineChartItem::calculatePoint(int index) const
57 {
48 {
58 const qreal deltaX = m_size.width()/m_domain.spanX();
49 const qreal deltaX = m_size.width()/m_domain.spanX();
59 const qreal deltaY = m_size.height()/m_domain.spanY();
50 const qreal deltaY = m_size.height()/m_domain.spanY();
@@ -62,34 +53,31 QPointF LineChartItem::calculatePoint(int index) const
62 return QPointF(x,y);
53 return QPointF(x,y);
63 }
54 }
64
55
65
56 QVector<QPointF> LineChartItem::calculateGeometryPoints() const
66 void LineChartItem::calculatePoints(QVector<QPointF>& points, QVector<int>& map) const
67 {
57 {
68 const qreal deltaX = m_size.width()/m_domain.spanX();
58 const qreal deltaX = m_size.width()/m_domain.spanX();
69 const qreal deltaY = m_size.height()/m_domain.spanY();
59 const qreal deltaY = m_size.height()/m_domain.spanY();
70
60
61 QVector<QPointF> points;
62 points.reserve(m_series->count());
71 for (int i = 0; i < m_series->count(); ++i) {
63 for (int i = 0; i < m_series->count(); ++i) {
72 qreal x = (m_series->x(i) - m_domain.m_minX)* deltaX;
64 qreal x = (m_series->x(i) - m_domain.m_minX)* deltaX;
73 qreal y = (m_series->y(i) - m_domain.m_minY)*-deltaY + m_size.height();
65 qreal y = (m_series->y(i) - m_domain.m_minY)*-deltaY + m_size.height();
74 map << i;
75 points << QPointF(x,y);
66 points << QPointF(x,y);
76 }
67 }
68 return points;
77 }
69 }
78
70
79 void LineChartItem::updateItem(QVector<QPointF>& points,QVector<int>& map)
71 void LineChartItem::updateItem(QVector<QPointF>& oldPoints,QVector<QPointF>& newPoints)
80 {
72 {
81 int diff = m_points.size() - points.size();
73 applyGeometry(newPoints);
82
74 oldPoints = newPoints;
83 if(diff>0) {
75 }
84 clearPoints(diff);
85 }
86 else if(diff<0) {
87 createPoints(-diff);
88 }
89
76
90 applyGeometry(points);
77 void LineChartItem::updateItem(QVector<QPointF>& oldPoints,int index,QPointF& newPoint)
91 m_points=points;
78 {
92 m_map=map;
79 oldPoints.replace(index,newPoint);
80 applyGeometry(oldPoints);
93 }
81 }
94
82
95 void LineChartItem::applyGeometry(QVector<QPointF>& points)
83 void LineChartItem::applyGeometry(QVector<QPointF>& points)
@@ -103,6 +91,7 void LineChartItem::applyGeometry(QVector<QPointF>& points)
103 path.moveTo(point);
91 path.moveTo(point);
104 QGraphicsItem* item = items.at(0);
92 QGraphicsItem* item = items.at(0);
105 item->setPos(point.x()-1,point.y()-1);
93 item->setPos(point.x()-1,point.y()-1);
94 if(!m_clipRect.contains(point)) item->setVisible(false);
106
95
107 for(int i=1 ; i< points.size();i++) {
96 for(int i=1 ; i< points.size();i++) {
108 QGraphicsItem* item = items.at(i);
97 QGraphicsItem* item = items.at(i);
@@ -129,104 +118,79 void LineChartItem::handlePointAdded(int index)
129 Q_ASSERT(index<m_series->count());
118 Q_ASSERT(index<m_series->count());
130 Q_ASSERT(index>=0);
119 Q_ASSERT(index>=0);
131
120
132 if(m_map.contains(index)){
121 QPointF point = calculateGeometryPoint(index);
133
122 createPoints(1);
134 int i = m_map.indexOf(index);
123 QVector<QPointF> points = m_points;
135
124 points.insert(index,point);
136 QPointF point = calculatePoint(index);
125 updateItem(m_points,points);
137
126 update();
138 if(point.isNull()) return;
139
140 QVector<QPointF> points = m_points;
141 points.insert(i,point);
142
143 for(int j=i;j<m_map.count();j++)
144 m_map[j]-=1;
145
146 updateItem(points,m_map);
147
148 }else{
149 //ignore
150 return;
151 }
152 update();
153 }
127 }
154 void LineChartItem::handlePointRemoved(int index)
128 void LineChartItem::handlePointRemoved(int index)
155 {
129 {
156 Q_ASSERT(index<m_series->count());
130 Q_ASSERT(index<m_series->count());
157 Q_ASSERT(index>=0);
131 Q_ASSERT(index>=0);
158
132 QPointF point = calculateGeometryPoint(index);
159 if(m_map.contains(index)){
133 clearPoints(1);
160
134 QVector<QPointF> points = m_points;
161 int i = m_map.value(index);
135 points.remove(index);
162
136 updateItem(m_points,points);
163 QVector<QPointF> points = m_points;
137 update();
164 points.remove(i);
165
166 for(int j=i;j<m_map.count();j++)
167 m_map[j]+=1;
168
169 m_map<<
170
171 updateItem(points,m_map);
172
173 }else{
174 //ignore
175 return;
176 }
177 update();
178 }
138 }
179
139
180 void LineChartItem::handlePointReplaced(int index)
140 void LineChartItem::handlePointReplaced(int index)
181 {
141 {
182 Q_ASSERT(index<m_series->count());
142 Q_ASSERT(index<m_series->count());
183 Q_ASSERT(index>=0);
143 Q_ASSERT(index>=0);
184
144 QPointF point = calculateGeometryPoint(index);
185 if(m_map.contains(index)){
145 updateItem(m_points,index,point);
186
187 int i = m_map.indexOf(index);
188
189 QPointF point = calculatePoint(index);
190
191 QVector<QPointF> points = m_points;
192 points.replace(i,point);
193
194 updateItem(points,m_map);
195
196 }else{
197 //ignore
198 return;
199 }
200 update();
146 update();
201 }
147 }
202
148
203 void LineChartItem::handleDomainChanged(const Domain& domain)
149 void LineChartItem::handleDomainChanged(const Domain& domain)
204 {
150 {
151
205 m_domain = domain;
152 m_domain = domain;
153
206 if(m_domain.isEmpty()) return;
154 if(m_domain.isEmpty()) return;
155 if(!m_clipRect.isValid()) return;
207
156
208 QVector<QPointF> points;
157 QVector<QPointF> points = calculateGeometryPoints();
209 QVector<int> map;
158
210 calculatePoints(points,map);
159 int diff = m_points.size() - points.size();
211 updateItem(points,map);
160
161 if(diff>0) {
162 clearPoints(diff);
163 }
164 else if(diff<0) {
165 createPoints(-diff);
166 }
167
168 updateItem(m_points,points);
212 update();
169 update();
213 }
170 }
214
171
215 void LineChartItem::handleGeometryChanged(const QRectF& rect)
172 void LineChartItem::handleGeometryChanged(const QRectF& rect)
216 {
173 {
217 Q_ASSERT(rect.isValid());
174 Q_ASSERT(rect.isValid());
218 m_size=rect.size();
175 m_size=rect.size();
219 m_clipRect=rect.translated(-rect.topLeft());
176 m_clipRect=rect.translated(-rect.topLeft());
220 setPos(rect.topLeft());
177 setPos(rect.topLeft());
221
178
222 if(m_domain.isEmpty()) return;
179 if(m_domain.isEmpty()) return;
223 if(m_points.isEmpty()) return;
224
180
225 QVector<QPointF> points;
181 QVector<QPointF> points = calculateGeometryPoints();
226 QVector<int> map;
182
227 calculatePoints(points,map);
183 int diff = m_points.size() - points.size();
228 updateItem(points,map);
184
229 update();
185 if(diff>0) {
186 clearPoints(diff);
187 }
188 else if(diff<0) {
189 createPoints(-diff);
190 }
191
192 updateItem(m_points,points);
193 update();
230 }
194 }
231
195
232 void LineChartItem::handleSeriesUpdated()
196 void LineChartItem::handleSeriesUpdated()
@@ -238,6 +202,19 void LineChartItem::handleSeriesUpdated()
238 update();
202 update();
239 }
203 }
240
204
205 //painter
206
207 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
208 {
209 Q_UNUSED(widget);
210 Q_UNUSED(option);
211 painter->save();
212 painter->setPen(m_pen);
213 painter->setClipRect(m_clipRect);
214 painter->drawPath(m_path);
215 painter->restore();
216 }
217
241
218
242 #include "moc_linechartitem_p.cpp"
219 #include "moc_linechartitem_p.cpp"
243
220
@@ -29,22 +29,18 public slots:
29 void handlePointAdded(int index);
29 void handlePointAdded(int index);
30 void handlePointRemoved(int index);
30 void handlePointRemoved(int index);
31 void handlePointReplaced(int index);
31 void handlePointReplaced(int index);
32 void handleSeriesUpdated();
32 void handleDomainChanged(const Domain& domain);
33 void handleDomainChanged(const Domain& domain);
33 void handleGeometryChanged(const QRectF& size);
34 void handleGeometryChanged(const QRectF& size);
34
35
35 protected:
36 public:
36 virtual void addPoint(const QPointF& );
37 virtual void updateItem(QVector<QPointF>& oldPoints,QVector<QPointF>& newPoints);
37 virtual void addPoints(const QVector<QPointF>& points);
38 virtual void updateItem(QVector<QPointF>& oldPoints,int index,QPointF& newPoint);
38 virtual void removePoint(const QPointF& point);
39 void applyGeometry(QVector<QPointF>& points);
39 virtual void replacePoint(const QPointF& oldPoint, const QPointF& newPoint);
40 void createPoints(int count);
40 virtual void replacePoint(int index,const QPointF& point);
41 void clearPoints(int count);
41 virtual void updateItem(QVector<QPointF>& points,QVector<int>& map);
42 QPointF calculateGeometryPoint(int index) const;
42 virtual void applyGeometry(QVector<QPointF>& points);
43 QVector<QPointF> calculateGeometryPoints() const;
43
44 void clear();
45
46 QPointF calculatePoint(int index) const;
47 void calculatePoints(QVector<QPointF>& points,QVector<int>& map) const;
48
44
49 private:
45 private:
50 ChartPresenter* m_presenter;
46 ChartPresenter* m_presenter;
@@ -53,9 +49,8 private:
53 QRectF m_rect;
49 QRectF m_rect;
54 QRectF m_clipRect;
50 QRectF m_clipRect;
55 Domain m_domain;
51 Domain m_domain;
56 QList<QGraphicsItem*> m_items;
52 QGraphicsItemGroup m_items;
57 QVector<QPointF> m_points;
53 QVector<QPointF> m_points;
58 QVector<int> m_map;
59 QLineSeries* m_series;
54 QLineSeries* m_series;
60 QPen m_pen;
55 QPen m_pen;
61
56
General Comments 0
You need to be logged in to leave comments. Login now