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