void CustomGraphicsScene::appendCard(int _cardType, const QString& _title, const QString& _description)
{
	QPointF scenePosition = sceneRect().center();

	//
	// Если выделена карточка
	//
	CardShape* selectedCard = nullptr;
	CardShape* previousCard = nullptr;
	CardShape* nextCard = nullptr;
	CardShape* parentCard = nullptr;
	if (!selectedItems().isEmpty()
		&& selectedItems().size() == 1
		&& (selectedCard = dynamic_cast<CardShape*>(selectedItems().last()))) {
		//
		// Если карточка вложена в группирующую, то расширяем родителя и вкладываем карту в него
		//
		if (selectedCard->parentItem() != nullptr) {
			//
			// Запомним родителя
			//
			parentCard = dynamic_cast<CardShape*>(selectedCard->parentItem());
		}

		//
		// Если выделен группирующий элемент, то соединять будем с последним из его детей
		//
		if (hasCards(selectedCard)) {
			selectedCard = dynamic_cast<CardShape*>(lastCard(selectedCard));
		}

		//
		// Предыдущей будет выделенная
		//
		previousCard = selectedCard;

		//
		// Настроим позицию для добавления новой карточки
		//
		scenePosition = previousCard->scenePos();
		scenePosition.setX(scenePosition.x() + previousCard->boundingRect().width() + SHAPE_MOVE_DELTA);
		scenePosition.setY(scenePosition.y() + previousCard->boundingRect().height() + SHAPE_MOVE_DELTA);

		//
		// Определим карточку, которая будет следовать за новой
		//
		Flow* flow = cardFlow(previousCard, CARD_ON_FLOW_START);
		if (flow != nullptr) {
			nextCard = dynamic_cast<CardShape*>(flow->endShape());
			removeShape(flow);
		}
	}
	//
	// В противном случае добавляем карточку после самой последней карточки, если карточки уже есть
	//
	else if (hasCards()) {
		//
		// Определим последнюю карточку
		//
		Shape* lastCardShape = lastCard();
		previousCard = dynamic_cast<CardShape*>(lastCardShape);

		//
		// Настроим позицию для добавления новой карточки
		//
		scenePosition = previousCard->scenePos();
		scenePosition.setX(scenePosition.x() + previousCard->boundingRect().width() + SHAPE_MOVE_DELTA);
		scenePosition.setY(scenePosition.y() + previousCard->boundingRect().height() + SHAPE_MOVE_DELTA);
	}
	//
	// В противном случае добавляем карточку по середине видимой части сцены, если подключены представления
	//
	else if (!views().isEmpty()) {
		if (QGraphicsView* view = views().last()) {
			const QRect viewportRect(0, 0, view->viewport()->width(), view->viewport()->height());
			const QRectF visibleSceneRect = view->mapToScene(viewportRect).boundingRect();
			scenePosition = visibleSceneRect.center();
		}
	}

	//
	// Добавляем карточку
	//
	CardShape* newCard = new CardShape((CardShape::CardType)_cardType, _title, _description, scenePosition, parentCard);
	insertShape(newCard, previousCard);
	//
	// ... корректируем позицию вкладываемой карточки
	//
	if (parentCard != nullptr) {
		const QPointF newPos = parentCard->mapFromScene(newCard->scenePos());
		const QPointF newBottomRightPos = newPos + QPointF(newCard->boundingRect().width(), newCard->boundingRect().height());
		//
		newCard->setParentItem(parentCard);
		newCard->setPos(newPos);
		//
		// ... и масштабируем родителя, если нужно
		//
		if (!parentCard->contains(newBottomRightPos)) {
			QSizeF newSize = parentCard->size();
			if (newSize.width() <= newBottomRightPos.x()) {
				newSize.setWidth(newBottomRightPos.x() + SHAPE_MICROMOVE_DELTA);
			}
			if (newSize.height() <= newBottomRightPos.y()) {
				newSize.setHeight(newBottomRightPos.y() + SHAPE_MICROMOVE_DELTA);
			}
			parentCard->setSize(newSize);
		}
	}

	//
	// Соединяем с предыдущей
	//
	if (previousCard != nullptr) {
		appendShape(new ArrowFlow(previousCard, newCard, parentCard));
	}

	//
	// Соединяем со следующей
	//
	if (nextCard != nullptr) {
		appendShape(new ArrowFlow(newCard, nextCard, parentCard));
	}
}
void CustomGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* _event)
{
	//
	// Обработку производим только после перемещения мыши
	//
	if (m_afterMoving) {
		//
		// 1. Перемещение карточки на связь
		//
		QList<Shape*> selected = selectedShapes();
		CardShape* selectedCard = nullptr;
		ArrowFlow* selectedFlow = nullptr;
		if (selected.size() == 2) {
			if (dynamic_cast<CardShape*>(selected.first())) {
				selectedCard = dynamic_cast<CardShape*>(selected.first());
				selectedFlow = dynamic_cast<ArrowFlow*>(selected.last());
			} else {
				selectedCard = dynamic_cast<CardShape*>(selected.last());
				selectedFlow = dynamic_cast<ArrowFlow*>(selected.first());
			}
			//
			// Если это действительно перемещение карточки на связь
			//
			if (selectedCard != nullptr
				&& selectedFlow != nullptr
				&& selectedFlow->startShape() != selectedCard
				&& selectedFlow->endShape() != selectedCard) {
				//
				// Изымаем карточку из сцены
				//
				takeShape(selectedCard);

				//
				// Определяем элементы к которым теперь будет присоединена карточка
				//
				CardShape* previousCard = dynamic_cast<CardShape*>(selectedFlow->startShape());
				CardShape* nextCard = dynamic_cast<CardShape*>(selectedFlow->endShape());
				//
				// Заменяем старую связь на новые
				//
				removeShape(selectedFlow);
				appendShape(new ArrowFlow(previousCard, selectedCard));
				if (hasCards(selectedCard)) {
					appendShape(new ArrowFlow(lastCard(selectedCard), nextCard));
				} else {
					appendShape(new ArrowFlow(selectedCard, nextCard));
				}

				//
				// Меняем порядок следования фигур
				//
				insertShape(selectedCard, previousCard);

				//
				// Если предыдущая карточка вложена в группирующий элемент
				//
				QGraphicsItem* previousParentItem = previousCard->parentItem();
				QGraphicsItem* nextParentItem = nextCard->parentItem();
				bool handled = false;
				if (previousParentItem != nullptr) {
					const QRectF selectedCardRect = selectedCard->mapToScene(selectedCard->boundingRect()).boundingRect();
					const QRectF parentCardRect = previousParentItem->mapToScene(previousParentItem->boundingRect()).boundingRect();
					//
					// ... и если текущая карточка тоже помещена внутрь группирующего элемента
					//
					if (parentCardRect.contains(selectedCardRect)) {
						//
						// ... поместим её внутрь
						//
						const QPointF lastPos = selectedCard->scenePos();
						selectedCard->setParentItem(previousParentItem);
						selectedCard->setPos(previousParentItem->mapFromScene(lastPos));
						handled = true;
					}
				}
				//
				// Если следующая карточка вложена в группирующий элемент
				//
				if (!handled
					&& nextParentItem != nullptr) {
					const QRectF selectedCardRect = selectedCard->mapToScene(selectedCard->boundingRect()).boundingRect();
					const QRectF parentCardRect = nextParentItem->mapToScene(nextParentItem->boundingRect()).boundingRect();
					//
					// ... и если текущая карточка тоже помещена внутрь группирующего элемента
					//
					if (parentCardRect.contains(selectedCardRect)) {
						//
						// ... поместим её внутрь
						//
						const QPointF lastPos = selectedCard->scenePos();
						selectedCard->setParentItem(nextParentItem);
						selectedCard->setPos(nextParentItem->mapFromScene(lastPos));
						handled = true;
					}
				}
				//
				// Если не удалось вложить, то убираем родителя у элемента
				//
				if (!handled
					&& selectedCard->parentItem() != nullptr) {
					const QPointF lastPos = selectedCard->scenePos();
					selectedCard->setParentItem(nullptr);
					selectedCard->setPos(lastPos);
				}
			}
		}

		//
		// 2. Обработка вложения и вытаскивания элементов из групп сцен и папок
		//
		selected = selectedShapes();
		if (selected.size() == 1) {
			selectedCard = dynamic_cast<CardShape*>(selected.first());
			if (selectedCard != nullptr) {
				//
				// Определим, есть ли группирующий элемент, помеченный на вложение
				//
				CardShape* parentCard = nullptr;
				for (Shape* shape : shapes()) {
					if (CardShape* card = dynamic_cast<CardShape*>(shape)) {
						if (card->isOnInstertionState()) {
							parentCard = card;
							break;
						}
					}
				}

				//
				// Если это перемещение карточки внутри своего родителя, просто снимем режим выделения
				//
				if (parentCard != nullptr
					&& selectedCard->parentItem() == parentCard) {
					parentCard->setOnInstertionState(false);
				}
				//
				// В противном случае
				//
				else {
					//
					// Вложение
					//
					if (parentCard != nullptr) {
						//
						// Изымаем карточку из сцены
						//
						takeShape(selectedCard);

						//
						// Если у группирующего элемента есть дети, то связываем с последней карточкой
						//
						if (hasCards(parentCard)) {
							//
							// Определяем элементы к которым теперь будет присоединена карточка
							//
							CardShape* previousCard = dynamic_cast<CardShape*>(lastCard(parentCard));
							Flow* previousCardFlow = cardFlow(previousCard, CARD_ON_FLOW_START);
							if (previousCardFlow != nullptr) {
								CardShape* nextCard = dynamic_cast<CardShape*>(previousCardFlow->endShape());
								//
								// Заменяем старую связь на новые
								//
								removeShape(previousCardFlow);
								if (hasCards(selectedCard)) {
									appendShape(new ArrowFlow(lastCard(selectedCard), nextCard));
								} else {
									appendShape(new ArrowFlow(selectedCard, nextCard));
								}
							}
							appendShape(new ArrowFlow(previousCard, selectedCard));

							//
							// Меняем порядок следования фигур
							//
							insertShape(selectedCard, previousCard);
						}
						//
						// Если детей нет, то связываем непосредственно с группирующим элементом
						//
						else {
							//
							// Определяем элементы к которым теперь будет присоединена карточка
							//
							CardShape* previousCard = parentCard;
							Flow* previousCardFlow = cardFlow(previousCard, CARD_ON_FLOW_START);
							if (previousCardFlow != nullptr) {
								CardShape* nextCard = dynamic_cast<CardShape*>(previousCardFlow->endShape());
								//
								// Заменяем старую связь на новые
								//
								removeShape(previousCardFlow);
								if (hasCards(selectedCard)) {
									appendShape(new ArrowFlow(lastCard(selectedCard), nextCard));
								} else {
									appendShape(new ArrowFlow(selectedCard, nextCard));
								}
							}
							appendShape(new ArrowFlow(previousCard, selectedCard));

							//
							// Меняем порядок следования фигур
							//
							insertShape(selectedCard, previousCard);
						}

						//
						// Назначаем нового родителя
						//
						const QPointF lastPos = selectedCard->scenePos();
						selectedCard->setParentItem(parentCard);
						selectedCard->setPos(parentCard->mapFromScene(lastPos));
						parentCard->setOnInstertionState(false);
					}
					//
					// Вытаскивание - соединяем с последней карточкой в сценарии
					//
					else if (selectedCard->parentItem() != nullptr) {
						//
						// Изымаем карточку из сцены
						//
						takeShape(selectedCard);

						//
						// Определяем элемент к которому теперь будет присоединена карточка
						//
						CardShape* previousCard =  dynamic_cast<CardShape*>(lastCard());
						//
						// Добавляем связь
						//
						appendShape(new ArrowFlow(previousCard, selectedCard));

						//
						// Меняем порядок следования фигур
						//
						insertShape(selectedCard, previousCard);

						//
						// Убираем родителя
						//
						const QPointF lastPos = selectedCard->scenePos();
						selectedCard->setParentItem(nullptr);
						selectedCard->setPos(lastPos);
					}
				}
			}
		}
	}

	update();

	QGraphicsScene::mouseReleaseEvent(_event);

	m_afterMoving = false;
}
Shape* CustomGraphicsScene::takeShape(Shape* _shape, bool _removeCardFlows)
{
	if (m_shapes.contains(_shape)) {
		//
		// При необходимости соединяем между собой окружающие карточку элементы
		//
		if (_removeCardFlows) {
			//
			// ... если это карточка, конечно
			//
			if (CardShape* cardShape = dynamic_cast<CardShape*>(_shape)) {
				//
				// Определяем элементы, с которыми соединена карточка, чтобы соеденить их между собой
				//
				Flow* startFlow = cardFlow(cardShape, CARD_ON_FLOW_END);
				Flow* endFlow = nullptr;
				if (hasCards(cardShape)) {
					endFlow = cardFlow(lastCard(cardShape), CARD_ON_FLOW_START);
				} else {
					endFlow = cardFlow(cardShape, CARD_ON_FLOW_START);
				}
				//
				// ... если есть связь в оба конца, соединяем карточки на концах этих связей
				//
				if (startFlow != nullptr && endFlow != nullptr) {
					startFlow->setEndShape(endFlow->endShape());
					removeShape(endFlow);
				}
				//
				// ... если есть связь только в начале, просто уберём эту связь
				//
				else if (startFlow != nullptr) {
					removeShape(startFlow);
				}
				//
				// ... если есть связь только в конце, просто уберём эту связь
				//
				else {
					removeShape(endFlow);
				}
			}
		}

		//
		// Извлекаем фигуру
		//
		disconnect(_shape, SIGNAL(stateIsAboutToBeChangedByUser()), this, SIGNAL(stateChangedByUser()));
		m_shapes.removeAll(_shape);
		//
		// ... извлекаем все вложенные карточки
		//
		for (QGraphicsItem* childItem : _shape->childItems()) {
			if (CardShape* childCard = dynamic_cast<CardShape*>(childItem)) {
				const bool DONT_REMOVE_FLOWS = false;
				takeShape(childCard, DONT_REMOVE_FLOWS);
			}
		}
		return _shape;
	}

	return NULL;
}