void AnnotationSettings::updateFontIfUniform()
{
	bool uniformFontFamily = true;
	bool uniformSize = true;
	bool uniformColor = true;

	QTextDocument *doc = _ui->content->document();

	QTextBlock b = doc->firstBlock();
	QTextCharFormat fmt1;
	bool first=true;

	// Check all character formats in all blocks. If they are the same,
	// we can reset the font for the wole document.
	while(b.isValid()) {
		for(const QTextLayout::FormatRange &fr : b.textFormats()) {

			if(first) {
				fmt1 = fr.format;
				first = false;

			} else {
				uniformFontFamily &= fr.format.fontFamily() == fmt1.fontFamily();
				uniformSize &= fr.format.fontPointSize() == fmt1.fontPointSize();
				uniformColor &= fr.format.foreground() == fmt1.foreground();
			}
		}
		b = b.next();
	}

	resetContentFont(uniformFontFamily, uniformSize, uniformColor);
}
QString DocumentImporter::importScenario(const ImportParameters& _importParameters) const
{
	//
	// Преобразовать заданный документ в QTextDocument
	//
	QTextDocument documentForImport;
	QFile documentFile(_importParameters.filePath);
	documentFile.open(QIODevice::ReadOnly);
	FormatReader* reader = FormatManager::createReader(&documentFile);
	reader->read(&documentFile, &documentForImport);

	//
	// Найти минимальный отступ слева для всех блоков
	// ЗАЧЕМ: во многих программах (Final Draft, Screeviner) сделано так, что поля
	//		  задаются за счёт оступов. Получается что и заглавие сцены и описание действия
	//		  имеют отступы. Так вот это и будет минимальным отступом, который не будем считать
	//
	int minLeftMargin = 1000;
	{
		QTextCursor cursor(&documentForImport);
		while (!cursor.atEnd()) {
			if (minLeftMargin > cursor.blockFormat().leftMargin()) {
				minLeftMargin = cursor.blockFormat().leftMargin();
			}

			cursor.movePosition(QTextCursor::NextBlock);
			cursor.movePosition(QTextCursor::EndOfBlock);
		}
	}

	//
	// FIXME: много чего дублируется с ScenarioXml
	//

	//
	// Преобразовать его в xml-строку
	//
	QString scenarioXml;
	QTextCursor cursor(&documentForImport);

	QXmlStreamWriter writer(&scenarioXml);
	writer.writeStartDocument();
	writer.writeStartElement(NODE_SCENARIO);
	writer.writeAttribute(ATTRIBUTE_VERSION, "1.0");

	//
	// Для каждого блока текста определяем тип
	//
	// ... последний стиль блока
	ScenarioBlockStyle::Type lastBlockType = ScenarioBlockStyle::Undefined;
	// ... количество пустых строк
	int emptyLines = 0;
	do {
		cursor.movePosition(QTextCursor::EndOfBlock);

		//
		// Если в блоке есть текст
		//
		if (!cursor.block().text().simplified().isEmpty()) {
			//
			// ... определяем тип
			//
			const ScenarioBlockStyle::Type blockType =
				::typeForTextCursor(cursor, lastBlockType, emptyLines, minLeftMargin,
					_importParameters.outline);
			const QString blockTypeName = ScenarioBlockStyle::typeName(blockType);
			QString blockText = cursor.block().text().simplified();

			//
			// ... запишем данные в строку
			//
			writer.writeStartElement(blockTypeName);

			//
			// Если текущий тип "Время и место" и нужно удалить номер сцены, то делаем это
			//
			if (blockType == ScenarioBlockStyle::SceneHeading
				&& _importParameters.removeScenesNumbers){
				blockText = blockText.toUpper();
				QRegularExpressionMatch match = START_FROM_NUMBER_CHECKER.match(blockText);
				if (match.hasMatch()) {
					blockText = blockText.mid(match.capturedEnd());
				}
			}

			//
			// Выполняем корректировки
			//
			blockText = ::clearBlockText(blockType, blockText);

			//
			// Пишем текст
			//
			writer.writeStartElement(NODE_VALUE);
			writer.writeCDATA(blockText);
			writer.writeEndElement();

			//
			// Пишем редакторские комментарии
			//
			if (_importParameters.saveReviewMarks) {
				const QTextBlock currentBlock = cursor.block();
				if (!currentBlock.textFormats().isEmpty()) {
					writer.writeStartElement(NODE_REVIEW_GROUP);
					foreach (const QTextLayout::FormatRange& range, currentBlock.textFormats()) {
						//
						// Всё, кроме стандартного
						//
						if (range.format.boolProperty(Docx::IsForeground)
							|| range.format.boolProperty(Docx::IsBackground)
							|| range.format.boolProperty(Docx::IsHighlight)
							|| range.format.boolProperty(Docx::IsComment)) {
							writer.writeStartElement(NODE_REVIEW);
							writer.writeAttribute(ATTRIBUTE_REVIEW_FROM, QString::number(range.start));
							writer.writeAttribute(ATTRIBUTE_REVIEW_LENGTH, QString::number(range.length));
							if (range.format.hasProperty(QTextFormat::ForegroundBrush)) {
								writer.writeAttribute(ATTRIBUTE_REVIEW_COLOR,
													  range.format.foreground().color().name());
							}
							if (range.format.hasProperty(QTextFormat::BackgroundBrush)) {
								writer.writeAttribute(ATTRIBUTE_REVIEW_BGCOLOR,
													  range.format.background().color().name());
							}
							writer.writeAttribute(ATTRIBUTE_REVIEW_IS_HIGHLIGHT,
												  range.format.boolProperty(Docx::IsHighlight) ? "true" : "false");
							//
							// ... комментарии
							//
							const QStringList comments = range.format.property(Docx::Comments).toStringList();
							const QStringList authors = range.format.property(Docx::CommentsAuthors).toStringList();
							const QStringList dates = range.format.property(Docx::CommentsDates).toStringList();
							for (int commentIndex = 0; commentIndex < comments.size(); ++commentIndex) {
								writer.writeEmptyElement(NODE_REVIEW_COMMENT);
								writer.writeAttribute(ATTRIBUTE_REVIEW_COMMENT, comments.at(commentIndex));
								writer.writeAttribute(ATTRIBUTE_REVIEW_AUTHOR, authors.at(commentIndex));
								writer.writeAttribute(ATTRIBUTE_REVIEW_DATE, dates.at(commentIndex));
							}
							//
							writer.writeEndElement();
						}
					}
					writer.writeEndElement();
				}
			}
			//
			// ... конец абзаца
			//
			writer.writeEndElement();

			//
			// Запомним последний стиль блока и обнулим счётчик пустых строк
			//
			lastBlockType = blockType;
			emptyLines = 0;
		}
		//
		// Если в блоке нет текста, то увеличиваем счётчик пустых строк
		//
		else {
			++emptyLines;
		}

		cursor.movePosition(QTextCursor::NextCharacter);
	} while (!cursor.atEnd());