]> git.sesse.net Git - kdenlive/blob - src/abstractgroupitem.cpp
Fix timeline move with Qt 4.6
[kdenlive] / src / abstractgroupitem.cpp
1 /***************************************************************************
2  *   Copyright (C) 2008 by Marco Gittler (g.marco@freenet.de)              *
3  *   Copyright (C) 2008 by Jean-Baptiste Mardelle (jb@kdenlive.org)        *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
19  ***************************************************************************/
20
21 #include "abstractgroupitem.h"
22 #include "abstractclipitem.h"
23 #include "kdenlivesettings.h"
24 #include "customtrackscene.h"
25 #include "customtrackview.h"
26
27 #include <KDebug>
28
29 #include <QPainter>
30 #include <QStyleOptionGraphicsItem>
31 #include <QDomDocument>
32 #include <QMimeData>
33 #include <QGraphicsSceneMouseEvent>
34
35 AbstractGroupItem::AbstractGroupItem(double /* fps */) :
36         QObject(),
37         QGraphicsItemGroup()
38 {
39     setZValue(1);
40     setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
41 #if QT_VERSION >= 0x040600
42     setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
43 #endif
44     setAcceptDrops(true);
45 }
46
47 int AbstractGroupItem::type() const
48 {
49     return GROUPWIDGET;
50 }
51
52 int AbstractGroupItem::track() const
53 {
54     return (int)(scenePos().y() / KdenliveSettings::trackheight());
55 }
56
57 CustomTrackScene* AbstractGroupItem::projectScene()
58 {
59     if (scene()) return static_cast <CustomTrackScene*>(scene());
60     return NULL;
61 }
62
63 QPainterPath AbstractGroupItem::clipGroupShape(QPointF offset) const
64 {
65     QPainterPath path;
66     QList<QGraphicsItem *> children = childItems();
67     for (int i = 0; i < children.count(); i++) {
68         if (children.at(i)->type() == AVWIDGET) {
69             QRectF r(children.at(i)->sceneBoundingRect());
70             r.translate(offset);
71             path.addRect(r);
72         }
73     }
74     return path;
75 }
76
77 QPainterPath AbstractGroupItem::transitionGroupShape(QPointF offset) const
78 {
79     QPainterPath path;
80     QList<QGraphicsItem *> children = childItems();
81     for (int i = 0; i < children.count(); i++) {
82         if (children.at(i)->type() == TRANSITIONWIDGET) {
83             QRectF r(children.at(i)->sceneBoundingRect());
84             r.translate(offset);
85             path.addRect(r);
86         }
87     }
88     return path;
89 }
90
91 void AbstractGroupItem::addItem(QGraphicsItem * item)
92 {
93     addToGroup(item);
94     //fixItemRect();
95 }
96
97 void AbstractGroupItem::fixItemRect()
98 {
99     QPointF start = boundingRect().topLeft();
100     if (start != QPointF(0, 0)) {
101         translate(0 - start.x(), 0 - start.y());
102         setPos(start);
103     }
104 }
105
106 /*ItemInfo AbstractGroupItem::info() const {
107     ItemInfo itemInfo;
108     itemInfo.startPos = m_startPos;
109     itemInfo.track = m_track;
110     return itemInfo;
111 }*/
112
113 // virtual
114 void AbstractGroupItem::paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *)
115 {
116     const double scale = option->matrix.m11();
117     QColor bgcolor(100, 100, 200, 100);
118     QRectF bound = option->exposedRect.adjusted(0, 0, 1, 1);
119     p->setClipRect(bound);
120     p->fillRect(option->exposedRect, bgcolor);
121     QPen pen = p->pen();
122     pen.setColor(QColor(200, 90, 90));
123     pen.setStyle(Qt::DashLine);
124     pen.setWidthF(0.0);
125     //pen.setCosmetic(true);
126     p->setPen(pen);
127     p->drawRect(boundingRect().adjusted(0, 0, - 1 / scale, 0));
128 }
129
130 //virtual
131 QVariant AbstractGroupItem::itemChange(GraphicsItemChange change, const QVariant &value)
132 {
133     if (change == ItemPositionChange && scene() && parentItem() == 0) {
134         // calculate new position.
135         const int trackHeight = KdenliveSettings::trackheight();
136         QPointF start = sceneBoundingRect().topLeft();
137         QPointF newPos = value.toPointF();
138         //kDebug()<<"REAL:"<<start.x()<<", PROPOSED:"<<(int)(start.x() - pos().x() + newPos.x());
139         int xpos = projectScene()->getSnapPointForPos((int)(start.x() + newPos.x() - pos().x()), KdenliveSettings::snaptopoints());
140
141         xpos = qMax(xpos, 0);
142         //kDebug()<<"GRP XPOS:"<<xpos<<", START:"<<start.x()<<",NEW:"<<newPos.x()<<"; SCENE:"<<scenePos().x()<<",POS:"<<pos().x();
143         newPos.setX((int)(pos().x() + xpos - (int) start.x()));
144
145         //int startTrack = (start.y() + trackHeight / 2) / trackHeight;
146
147         int realTrack = (start.y() + newPos.y() - pos().y()) / trackHeight;
148         int proposedTrack = newPos.y() / trackHeight;
149
150         int correctedTrack = qMin(realTrack, projectScene()->tracksCount() - (int)(boundingRect().height() + 5) / trackHeight);
151         correctedTrack = qMax(correctedTrack, 0);
152
153         proposedTrack += (correctedTrack - realTrack);
154
155         // Check if top item is a clip or a transition
156         int offset = 0;
157         int topTrack = -1;
158         QList<QGraphicsItem *> children = childItems();
159         for (int i = 0; i < children.count(); i++) {
160             int currentTrack = (int)(children.at(i)->scenePos().y() / trackHeight);
161             if (children.at(i)->type() == AVWIDGET) {
162                 if (topTrack == -1 || currentTrack <= topTrack) {
163                     offset = 0;
164                     topTrack = currentTrack;
165                 }
166             } else if (children.at(i)->type() == TRANSITIONWIDGET) {
167                 if (topTrack == -1 || currentTrack < topTrack) {
168                     offset = (int)(trackHeight / 3 * 2 - 1);
169                     topTrack = currentTrack;
170                 }
171             } else if (children.at(i)->type() == GROUPWIDGET) {
172                 QList<QGraphicsItem *> subchildren = children.at(i)->childItems();
173                 bool clipGroup = false;
174                 for (int j = 0; j < subchildren.count(); j++) {
175                     if (subchildren.at(j)->type() == AVWIDGET) {
176                         clipGroup = true;
177                         break;
178                     }
179                 }
180                 if (clipGroup) {
181                     if (topTrack == -1 || currentTrack <= topTrack) {
182                         offset = 0;
183                         topTrack = currentTrack;
184                     }
185                 } else {
186                     if (topTrack == -1 || currentTrack < topTrack) {
187                         offset = (int)(trackHeight / 3 * 2 - 1);
188                         topTrack = currentTrack;
189                     }
190                 }
191             }
192         }
193         newPos.setY((int)((proposedTrack) * trackHeight) + offset);
194         //if (newPos == start) return start;
195
196         /*if (newPos.x() < 0) {
197             // If group goes below 0, adjust position to 0
198             return QPointF(pos().x() - start.x(), pos().y());
199         }*/
200
201         QPainterPath shape = clipGroupShape(newPos - pos());
202         QList<QGraphicsItem*> collindingItems;
203         if (projectScene()->editMode() == NORMALEDIT) {
204             collindingItems = scene()->items(shape, Qt::IntersectsItemShape);
205             for (int i = 0; i < children.count(); i++) {
206                 collindingItems.removeAll(children.at(i));
207             }
208         }
209         if (!collindingItems.isEmpty()) {
210             bool forwardMove = xpos > start.x();
211             int offset = 0;
212             for (int i = 0; i < collindingItems.count(); i++) {
213                 QGraphicsItem *collision = collindingItems.at(i);
214                 if (collision->type() == AVWIDGET) {
215                     // Collision
216                     if (newPos.y() != pos().y()) {
217                         // Track change results in collision, restore original position
218                         return pos();
219                     }
220                     AbstractClipItem *item = static_cast <AbstractClipItem *>(collision);
221                     if (forwardMove) {
222                         // Moving forward, determine best pos
223                         QPainterPath clipPath;
224                         clipPath.addRect(item->sceneBoundingRect());
225                         QPainterPath res = shape.intersected(clipPath);
226                         offset = qMax(offset, (int)(res.boundingRect().width() + 0.5));
227                     } else {
228                         // Moving backward, determine best pos
229                         QPainterPath clipPath;
230                         clipPath.addRect(item->sceneBoundingRect());
231                         QPainterPath res = shape.intersected(clipPath);
232                         offset = qMax(offset, (int)(res.boundingRect().width() + 0.5));
233                     }
234                 }
235             }
236             if (offset > 0) {
237                 if (forwardMove) {
238                     newPos.setX(newPos.x() - offset);
239                 } else {
240                     newPos.setX(newPos.x() + offset);
241                 }
242                 // If there is still a collision after our position adjust, restore original pos
243                 collindingItems = scene()->items(clipGroupShape(newPos - pos()), Qt::IntersectsItemShape);
244                 for (int i = 0; i < children.count(); i++) {
245                     collindingItems.removeAll(children.at(i));
246                 }
247                 for (int i = 0; i < collindingItems.count(); i++)
248                     if (collindingItems.at(i)->type() == AVWIDGET) return pos();
249             }
250         }
251
252
253         shape = transitionGroupShape(newPos - pos());
254         collindingItems = scene()->items(shape, Qt::IntersectsItemShape);
255         for (int i = 0; i < children.count(); i++) {
256             collindingItems.removeAll(children.at(i));
257         }
258         if (collindingItems.isEmpty()) return newPos;
259         else {
260             bool forwardMove = xpos > start.x();
261             int offset = 0;
262             for (int i = 0; i < collindingItems.count(); i++) {
263                 QGraphicsItem *collision = collindingItems.at(i);
264                 if (collision->type() == TRANSITIONWIDGET) {
265                     // Collision
266                     if (newPos.y() != pos().y()) {
267                         // Track change results in collision, restore original position
268                         return pos();
269                     }
270                     AbstractClipItem *item = static_cast <AbstractClipItem *>(collision);
271                     if (forwardMove) {
272                         // Moving forward, determine best pos
273                         QPainterPath clipPath;
274                         clipPath.addRect(item->sceneBoundingRect());
275                         QPainterPath res = shape.intersected(clipPath);
276                         offset = qMax(offset, (int)(res.boundingRect().width() + 0.5));
277                     } else {
278                         // Moving backward, determine best pos
279                         QPainterPath clipPath;
280                         clipPath.addRect(item->sceneBoundingRect());
281                         QPainterPath res = shape.intersected(clipPath);
282                         offset = qMax(offset, (int)(res.boundingRect().width() + 0.5));
283                     }
284                 }
285             }
286             if (offset > 0) {
287                 if (forwardMove) {
288                     newPos.setX(newPos.x() - offset);
289                 } else {
290                     newPos.setX(newPos.x() + offset);
291                 }
292                 // If there is still a collision after our position adjust, restore original pos
293                 collindingItems = scene()->items(transitionGroupShape(newPos - pos()), Qt::IntersectsItemShape);
294                 for (int i = 0; i < children.count(); i++) {
295                     collindingItems.removeAll(children.at(i));
296                 }
297                 for (int i = 0; i < collindingItems.count(); i++)
298                     if (collindingItems.at(i)->type() == TRANSITIONWIDGET) return pos();
299             }
300         }
301         return newPos;
302     }
303     return QGraphicsItemGroup::itemChange(change, value);
304 }
305
306 //virtual
307 void AbstractGroupItem::dropEvent(QGraphicsSceneDragDropEvent * event)
308 {
309     QString effects = QString(event->mimeData()->data("kdenlive/effectslist"));
310     QDomDocument doc;
311     doc.setContent(effects, true);
312     QDomElement e = doc.documentElement();
313     CustomTrackView *view = (CustomTrackView *) scene()->views()[0];
314     if (view) view->slotAddGroupEffect(e, this);
315 }
316
317 //virtual
318 void AbstractGroupItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
319 {
320     event->setAccepted(event->mimeData()->hasFormat("kdenlive/effectslist"));
321 }
322
323 void AbstractGroupItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
324 {
325     Q_UNUSED(event);
326 }
327
328 // virtual
329 void AbstractGroupItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
330 {
331     if (event->modifiers() & Qt::ShiftModifier) {
332         // User want to do a rectangle selection, so ignore the event to pass it to the view
333         event->ignore();
334     } else QGraphicsItem::mousePressEvent(event);
335 }