1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "editor/scene/scene.h"
9
10 #include "editor/scene/scene_item_canvas.h"
11 #include "editor/scene/scene_item_line.h"
12 #include "editor/scene/scene_item_sticker.h"
13 #include "ui/rp_widget.h"
14
15 #include <QGraphicsSceneMouseEvent>
16
17 namespace Editor {
18 namespace {
19
20 using ItemPtr = std::shared_ptr<NumberedItem>;
21
SkipMouseEvent(not_null<QGraphicsSceneMouseEvent * > event)22 bool SkipMouseEvent(not_null<QGraphicsSceneMouseEvent*> event) {
23 return event->isAccepted() || (event->button() == Qt::RightButton);
24 }
25
26 } // namespace
27
Scene(const QRectF & rect)28 Scene::Scene(const QRectF &rect)
29 : QGraphicsScene(rect)
30 , _canvas(std::make_shared<ItemCanvas>())
31 , _lastZ(std::make_shared<float64>(9000.)) {
32 QGraphicsScene::addItem(_canvas.get());
33 _canvas->clearPixmap();
34
35 _canvas->grabContentRequests(
36 ) | rpl::start_with_next([=](ItemCanvas::Content &&content) {
37 const auto item = std::make_shared<ItemLine>(
38 std::move(content.pixmap));
39 item->setPos(content.position);
40 addItem(item);
41 _canvas->setZValue(++_lastLineZ);
42 }, _lifetime);
43 }
44
cancelDrawing()45 void Scene::cancelDrawing() {
46 _canvas->cancelDrawing();
47 }
48
addItem(ItemPtr item)49 void Scene::addItem(ItemPtr item) {
50 if (!item) {
51 return;
52 }
53 item->setNumber(_itemNumber++);
54 QGraphicsScene::addItem(item.get());
55 _items.push_back(std::move(item));
56 _addsItem.fire({});
57 }
58
removeItem(not_null<QGraphicsItem * > item)59 void Scene::removeItem(not_null<QGraphicsItem*> item) {
60 const auto it = ranges::find_if(_items, [&](const ItemPtr &i) {
61 return i.get() == item;
62 });
63 if (it == end(_items)) {
64 return;
65 }
66 removeItem(*it);
67 }
68
removeItem(const ItemPtr & item)69 void Scene::removeItem(const ItemPtr &item) {
70 item->setStatus(NumberedItem::Status::Removed);
71 _removesItem.fire({});
72 }
73
mousePressEvent(QGraphicsSceneMouseEvent * event)74 void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event) {
75 QGraphicsScene::mousePressEvent(event);
76 if (SkipMouseEvent(event)) {
77 return;
78 }
79 _canvas->handleMousePressEvent(event);
80 }
81
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)82 void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
83 QGraphicsScene::mouseReleaseEvent(event);
84 if (SkipMouseEvent(event)) {
85 return;
86 }
87 _canvas->handleMouseReleaseEvent(event);
88 }
89
mouseMoveEvent(QGraphicsSceneMouseEvent * event)90 void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
91 QGraphicsScene::mouseMoveEvent(event);
92 if (SkipMouseEvent(event)) {
93 return;
94 }
95 _canvas->handleMouseMoveEvent(event);
96 }
97
applyBrush(const QColor & color,float size)98 void Scene::applyBrush(const QColor &color, float size) {
99 _canvas->applyBrush(color, size);
100 }
101
addsItem() const102 rpl::producer<> Scene::addsItem() const {
103 return _addsItem.events();
104 }
105
removesItem() const106 rpl::producer<> Scene::removesItem() const {
107 return _removesItem.events();
108 }
109
items(Qt::SortOrder order) const110 std::vector<ItemPtr> Scene::items(
111 Qt::SortOrder order) const {
112 auto copyItems = _items;
113
114 ranges::sort(copyItems, [&](ItemPtr a, ItemPtr b) {
115 const auto numA = a->number();
116 const auto numB = b->number();
117 return (order == Qt::AscendingOrder) ? (numA < numB) : (numA > numB);
118 });
119
120 return copyItems;
121 }
122
attachedStickers() const123 std::vector<not_null<DocumentData*>> Scene::attachedStickers() const {
124 const auto allItems = items();
125
126 return ranges::views::all(
127 allItems
128 ) | ranges::views::filter([](const ItemPtr &i) {
129 return i->isVisible() && (i->type() == ItemSticker::Type);
130 }) | ranges::views::transform([](const ItemPtr &i) {
131 return static_cast<ItemSticker*>(i.get())->sticker();
132 }) | ranges::to_vector;
133 }
134
lastZ() const135 std::shared_ptr<float64> Scene::lastZ() const {
136 return _lastZ;
137 }
138
updateZoom(float64 zoom)139 void Scene::updateZoom(float64 zoom) {
140 for (const auto &item : items()) {
141 if (item->type() >= ItemBase::Type) {
142 static_cast<ItemBase*>(item.get())->updateZoom(zoom);
143 }
144 }
145 }
146
hasUndo() const147 bool Scene::hasUndo() const {
148 return ranges::any_of(_items, &NumberedItem::isNormalStatus);
149 }
150
hasRedo() const151 bool Scene::hasRedo() const {
152 return ranges::any_of(_items, &NumberedItem::isUndidStatus);
153 }
154
performUndo()155 void Scene::performUndo() {
156 const auto filtered = items(Qt::DescendingOrder);
157
158 const auto it = ranges::find_if(filtered, &NumberedItem::isNormalStatus);
159 if (it != filtered.end()) {
160 (*it)->setStatus(NumberedItem::Status::Undid);
161 }
162 }
163
performRedo()164 void Scene::performRedo() {
165 const auto filtered = items(Qt::AscendingOrder);
166
167 const auto it = ranges::find_if(filtered, &NumberedItem::isUndidStatus);
168 if (it != filtered.end()) {
169 (*it)->setStatus(NumberedItem::Status::Normal);
170 }
171 }
172
removeIf(Fn<bool (const ItemPtr &)> proj)173 void Scene::removeIf(Fn<bool(const ItemPtr &)> proj) {
174 auto copy = std::vector<ItemPtr>();
175 for (const auto &item : _items) {
176 const auto toRemove = proj(item);
177 if (toRemove) {
178 // Scene loses ownership of an item.
179 // It seems for some reason this line causes a crash. =(
180 // QGraphicsScene::removeItem(item.get());
181 } else {
182 copy.push_back(item);
183 }
184 }
185 _items = std::move(copy);
186 }
187
clearRedoList()188 void Scene::clearRedoList() {
189 for (const auto &item : _items) {
190 if (item->isUndidStatus()) {
191 item->setStatus(NumberedItem::Status::Removed);
192 }
193 }
194 }
195
save(SaveState state)196 void Scene::save(SaveState state) {
197 removeIf([](const ItemPtr &item) {
198 return item->isRemovedStatus()
199 && !item->hasState(SaveState::Keep)
200 && !item->hasState(SaveState::Save);
201 });
202
203 for (const auto &item : _items) {
204 item->save(state);
205 }
206 clearSelection();
207 cancelDrawing();
208 }
209
restore(SaveState state)210 void Scene::restore(SaveState state) {
211 removeIf([=](const ItemPtr &item) {
212 return !item->hasState(state);
213 });
214
215 for (const auto &item : _items) {
216 item->restore(state);
217 }
218 clearSelection();
219 cancelDrawing();
220 }
221
~Scene()222 Scene::~Scene() {
223 // Prevent destroying by scene of all items.
224 QGraphicsScene::removeItem(_canvas.get());
225 for (const auto &item : items()) {
226 // Scene loses ownership of an item.
227 QGraphicsScene::removeItem(item.get());
228 }
229 }
230
231 } // namespace Editor
232