language support

Thu, 03 Oct 2019 23:44:28 +0300

author
Teemu Piippo <teemu@hecknology.net>
date
Thu, 03 Oct 2019 23:44:28 +0300
changeset 6
73e448b2943d
parent 5
593a658cba8e
child 7
68443f5be176

language support

CMakeLists.txt file | annotate | diff | comparison | revisions
flags/en.png file | annotate | diff | comparison | revisions
flags/en.svg file | annotate | diff | comparison | revisions
flags/fi.png file | annotate | diff | comparison | revisions
flags/fi.svg file | annotate | diff | comparison | revisions
flags/make_flags.sh file | annotate | diff | comparison | revisions
flags/ru.png file | annotate | diff | comparison | revisions
flags/ru.svg file | annotate | diff | comparison | revisions
flags/sv.png file | annotate | diff | comparison | revisions
flags/sv.svg file | annotate | diff | comparison | revisions
languages.qrc file | annotate | diff | comparison | revisions
ldforge.qrc file | annotate | diff | comparison | revisions
locale/fi.ts file | annotate | diff | comparison | revisions
locale/ru.ts file | annotate | diff | comparison | revisions
locale/sv.ts file | annotate | diff | comparison | revisions
src/basics.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/mainwindow.cpp file | annotate | diff | comparison | revisions
src/mainwindow.h file | annotate | diff | comparison | revisions
src/mainwindow.ui file | annotate | diff | comparison | revisions
src/model.h file | annotate | diff | comparison | revisions
src/objecttypes/comment.cpp file | annotate | diff | comparison | revisions
src/objecttypes/comment.h file | annotate | diff | comparison | revisions
src/objecttypes/conditionaledge.cpp file | annotate | diff | comparison | revisions
src/objecttypes/conditionaledge.h file | annotate | diff | comparison | revisions
src/objecttypes/edge.cpp file | annotate | diff | comparison | revisions
src/objecttypes/edge.h file | annotate | diff | comparison | revisions
src/objecttypes/errorline.cpp file | annotate | diff | comparison | revisions
src/objecttypes/errorline.h file | annotate | diff | comparison | revisions
src/objecttypes/modelobject.cpp file | annotate | diff | comparison | revisions
src/objecttypes/modelobject.h file | annotate | diff | comparison | revisions
src/objecttypes/polygon.cpp file | annotate | diff | comparison | revisions
src/objecttypes/polygon.h file | annotate | diff | comparison | revisions
src/objecttypes/subfilereference.cpp file | annotate | diff | comparison | revisions
src/objecttypes/subfilereference.h file | annotate | diff | comparison | revisions
src/uuid.cpp file | annotate | diff | comparison | revisions
src/uuid.h file | annotate | diff | comparison | revisions
src/vertex.cpp file | annotate | diff | comparison | revisions
src/vertex.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Thu Oct 03 11:45:44 2019 +0300
+++ b/CMakeLists.txt	Thu Oct 03 23:44:28 2019 +0300
@@ -3,12 +3,13 @@
 #set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake")
 #include(cotire)
 find_package(Qt5Widgets REQUIRED)
+if (Qt5Widgets_VERSION VERSION_LESS 5.5.0)
+	message(FATAL_ERROR "Qt5 version 5.5 required")
+endif()
 find_package(Qt5Core REQUIRED)
 find_package(Qt5OpenGL REQUIRED)
 find_package(Qt5Network REQUIRED)
-if (Qt5Widgets_VERSION VERSION_LESS 5.5.0)
-	message(FATAL_ERROR "Qt5 version 5.5 required")
-endif()
+find_package(Qt5LinguistTools REQUIRED)
 set (CMAKE_AUTOMOC ON)
 find_package (OpenGL REQUIRED)
 add_custom_target (revision_check ALL
@@ -23,8 +24,7 @@
 	src/mainwindow.cpp
 	src/model.cpp
 	src/modeleditcontext.cpp
-	src/parser.cpp
-	src/uuid.cpp
+        src/parser.cpp
 	src/version.cpp
 	src/vertex.cpp
 	src/objecttypes/comment.cpp
@@ -43,8 +43,7 @@
 	src/mainwindow.h
 	src/model.h
 	src/modeleditcontext.h
-	src/parser.h
-	src/uuid.h
+        src/parser.h
 	src/version.h
 	src/vertex.h
 	src/objecttypes/comment.h
@@ -62,6 +61,19 @@
 # 	src/configurationoptions.txt
 # 	data/primitive-categories.cfg
 # )
+
+set(LDFORGE_LOCALES
+    locale/fi.ts
+    locale/sv.ts
+    locale/ru.ts
+)
+
+# Qt5LinguistTools
+qt5_create_translation(QM_FILES ${LDFORGE_SOURCES} ${LDFORGE_HEADERS} ${LDFORGE_FORMS} ${LDFORGE_LOCALES})
+add_custom_target(translations ALL DEPENDS ${QM_FILES})
+add_custom_target(resources ALL DEPENDS ${RESOURCE_FILES})
+add_dependencies(resources translations)
+
 set (LDFORGE_RESOURCES ldforge.qrc)
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -79,16 +91,28 @@
 endif()
 # qt5_add_resources (LDFORGE_QRC ${LDFORGE_RESOURCES})
 qt5_wrap_ui (LDFORGE_FORMS_HEADERS ${LDFORGE_FORMS})
+
+# Move languages.qrc into the build directory to bake the .qm-files into LDForge
+# so that they don't have to be shipped separately
+# https://stackoverflow.com/a/34798124
+set(LANGUAGES_QRC "languages.qrc")
+configure_file(${LANGUAGES_QRC} ${LANGUAGES_QRC} COPYONLY)
+qt5_add_resources(LDFORGE_QM_RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${LANGUAGES_QRC})
+
 add_executable (ldforge WIN32
     ${LDFORGE_SOURCES} 
     ${LDFORGE_HEADERS} 
 #     ${LDFORGE_OTHER_FILES}
-# 	${LDFORGE_QRC}
+    ${LDFORGE_RESOURCES}
+    ${LDFORGE_QRC}
+    ${LDFORGE_QM_RC_FILE}
 	${LDFORGE_FORMS_HEADERS}
 # 	${CMAKE_BINARY_DIR}/configuration.cpp
 )
+
 set_source_files_properties(${LDFORGE_HEADERS} PROPERTIES HEADER_FILE_ONLY TRUE)
 set_source_files_properties(${LDFORGE_OTHER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)
+set_source_files_properties(${LDFORGE_RESOURCES} PROPERTIES HEADER_FILE_ONLY TRUE)
 set_target_properties(ldforge PROPERTIES AUTOMOC 1)
 target_link_libraries(ldforge Qt5::Widgets Qt5::Network Qt5::OpenGL ${OPENGL_LIBRARIES})
 # cotire(ldforge)
Binary file flags/en.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flags/en.svg	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280" height="640" viewBox="0 0 10080 5040">
+<defs>
+<clipPath id="c">
+<path d="M0,0V1.5H7V3H6zM6,0H3V3.5H0V3z"/>
+</clipPath>
+<path id="Star7" d="M0,-360 69.421398,-144.155019 281.459334,-224.456329 155.988466,-35.603349 350.974048,80.107536 125.093037,99.758368 156.198146,324.348792 0,160 -156.198146,324.348792 -125.093037,99.758368 -350.974048,80.107536 -155.988466,-35.603349 -281.459334,-224.456329 -69.421398,-144.155019z"/>
+<path id="Star5" d="M0,-210 54.859957,-75.508253 199.721868,-64.893569 88.765275,28.841586 123.434903,169.893569 0,93.333333 -123.434903,169.893569 -88.765275,28.841586 -199.721868,-64.893569 -54.859957,-75.508253z"/>
+<use id="Cstar" xlink:href="#Star7" transform="scale(2.1)"/>
+</defs>
+<g transform="scale(840)">
+<rect width="12" height="6" fill="#00008b"/>
+<path d="M0,0 6,3M6,0 0,3" stroke="#fff" stroke-width="0.6"/>
+<path d="M0,0 6,3M6,0 0,3" stroke="#f00" stroke-width="0.4" clip-path="url(#c)"/>
+<path d="M3,0V3.5M0,1.5H7" stroke="#fff"/>
+<path d="M3,0V3.5M0,1.5H7" stroke="#f00" stroke-width="0.6"/>
+<path d="M0,3H6V0H8V4H0z" fill="#00008b"/>
+</g>
+<g fill="#fff">
+<use id="Comwlth" xlink:href="#Cstar" x="2520" y="3780"/>
+<use id="αCrucis" xlink:href="#Star7" x="7560" y="4200"/>
+<use id="βCrucis" xlink:href="#Star7" x="6300" y="2205"/>
+<use id="γCrucis" xlink:href="#Star7" x="7560" y="840"/>
+<use id="δCrucis" xlink:href="#Star7" x="8680" y="1869"/>
+<use id="εCrucis" xlink:href="#Star5" x="8064" y="2730"/>
+</g>
+</svg>
\ No newline at end of file
Binary file flags/fi.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flags/fi.svg	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="1800" height="1100">
+<rect width="1800" height="1100" fill="#fff"/>
+<rect width="1800" height="300" y="400" fill="#003580"/>
+<rect width="300" height="1100" x="500" fill="#003580"/>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flags/make_flags.sh	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,7 @@
+#!/bin/bash
+for flag in *.svg
+do
+	flag_png="${flag%.svg}.png"
+	echo "$flag => $flag_png"
+	inkscape -z -e ${flag_png} -w 64 $flag
+done
Binary file flags/ru.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flags/ru.svg	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9 6" width="900" height="600"><rect fill="#fff" width="9" height="3"/><rect fill="#d52b1e" y="3" width="9" height="3"/><rect fill="#0039a6" y="2" width="9" height="2"/></svg>
\ No newline at end of file
Binary file flags/sv.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flags/sv.svg	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1000" viewBox="0 0 16 10">
+<rect width="16" height="10" fill="#006aa7"/>
+<rect width="2" height="10" x="5" fill="#fecc00"/>
+<rect width="16" height="2" y="4" fill="#fecc00"/>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/languages.qrc	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,7 @@
+<RCC>
+    <qresource prefix="/locale">
+        <file>fi.qm</file>
+        <file>ru.qm</file>
+        <file>sv.qm</file>
+    </qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ldforge.qrc	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,8 @@
+<RCC>
+    <qresource prefix="/">
+        <file>flags/en.png</file>
+        <file>flags/fi.png</file>
+        <file>flags/ru.png</file>
+        <file>flags/sv.png</file>
+    </qresource>
+</RCC>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/locale/fi.ts	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="fi">
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>MainWindow</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>File</source>
+        <translation>Tiedosto</translation>
+    </message>
+    <message>
+        <source>Quit</source>
+        <translation>Poistu</translation>
+    </message>
+    <message>
+        <source>Open…</source>
+        <translation>Avaa...</translation>
+    </message>
+    <message>
+        <source>Ctrl+O</source>
+        <translation>Ctrl+O</translation>
+    </message>
+    <message>
+        <source>New</source>
+        <translation>Uusi</translation>
+    </message>
+    <message>
+        <source>Ctrl+N</source>
+        <translation>Ctrl+N</translation>
+    </message>
+    <message>
+        <source>Language</source>
+        <translation>Kieli</translation>
+    </message>
+    <message>
+        <source>Open model</source>
+        <translation type="unfinished">Avaa malli</translation>
+    </message>
+    <message>
+        <source>LDraw models (*.ldr *.dat)</source>
+        <translation>LDraw-mallit (*.ldr *.dat)</translation>
+    </message>
+    <message>
+        <source>Problem opening file</source>
+        <translation>Ongelma tiedoston avaamisessa</translation>
+    </message>
+    <message>
+        <source>Could not open %1: %2</source>
+        <translation>Ei voitu avata %1: %2</translation>
+    </message>
+</context>
+</TS>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/locale/ru.ts	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru">
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>MainWindow</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>File</source>
+        <translation>Файл</translation>
+    </message>
+    <message>
+        <source>Quit</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Open…</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Ctrl+O</source>
+        <translation>Ctrl+O</translation>
+    </message>
+    <message>
+        <source>New</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>Ctrl+N</source>
+        <translation>Ctrl+N</translation>
+    </message>
+    <message>
+        <source>Language</source>
+        <translation>Язык</translation>
+    </message>
+    <message>
+        <source>Open model</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>LDraw models (*.ldr *.dat)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Problem opening file</source>
+        <translation></translation>
+    </message>
+    <message>
+        <source>Could not open %1: %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+</TS>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/locale/sv.ts	Thu Oct 03 23:44:28 2019 +0300
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="sv">
+<context>
+    <name>MainWindow</name>
+    <message>
+        <source>MainWindow</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <source>File</source>
+        <translation>Arkiv</translation>
+    </message>
+    <message>
+        <source>Quit</source>
+        <translation>Avsluta</translation>
+    </message>
+    <message>
+        <source>Open…</source>
+        <translation>Öppna...</translation>
+    </message>
+    <message>
+        <source>Ctrl+O</source>
+        <translation>Ctrl+O</translation>
+    </message>
+    <message>
+        <source>New</source>
+        <translation>Ny</translation>
+    </message>
+    <message>
+        <source>Ctrl+N</source>
+        <translation>Ctrl+N</translation>
+    </message>
+    <message>
+        <source>Language</source>
+        <translation>Språk</translation>
+    </message>
+    <message>
+        <source>Open model</source>
+        <translation>Öppna modell</translation>
+    </message>
+    <message>
+        <source>LDraw models (*.ldr *.dat)</source>
+        <translation>LDrawmodeller (*.ldr *.dat)</translation>
+    </message>
+    <message>
+        <source>Problem opening file</source>
+        <translation>Problem att öppna filen</translation>
+    </message>
+    <message>
+        <source>Could not open %1: %2</source>
+        <translation>Kunde inte öppna %1: %2</translation>
+    </message>
+</context>
+</TS>
--- a/src/basics.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/basics.h	Thu Oct 03 23:44:28 2019 +0300
@@ -86,3 +86,15 @@
 {
 	return (x << 20) | ((x >> 12) & 0x000000ff);
 }
+
+template<typename T, typename... Rest>
+QString format(const QString &format_string, T&& arg, Rest&&... rest)
+{
+	return format(format_string.arg(arg), std::forward<Rest>(rest)...);
+}
+
+template<typename T>
+QString format(const QString &format_string, T&& arg)
+{
+	return format_string.arg(arg);
+}
--- a/src/main.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/main.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -1,4 +1,5 @@
 #include <QApplication>
+#include <QDir>
 #include "main.h"
 #include "mainwindow.h"
 
--- a/src/mainwindow.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/mainwindow.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -9,10 +9,13 @@
 	ui{std::make_unique<Ui_MainWindow>()},
 	documents{this}
 {
-	ui->setupUi(this);
+	this->ui->setupUi(this);
 	connect(ui->actionNew, &QAction::triggered, this, &MainWindow::newModel);
+	connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::openModel);
 	connect(ui->actionQuit, &QAction::triggered, this, &QMainWindow::close);
-	updateTitle();
+	this->updateTitle();
+	this->loadLocales();
+	changeLanguage(QLocale::system().name());
 }
 
 MainWindow::~MainWindow()
@@ -30,7 +33,7 @@
 void MainWindow::openModel()
 {
 	const QString path = QFileDialog::getOpenFileName(
-		this, "Open model", "", "LDraw models (*.ldr *.dat)");
+		this, tr("Open model"), "", tr("LDraw models (*.ldr *.dat)"));
 	if (not path.isEmpty())
 	{
 		QFile file{path};
@@ -41,8 +44,8 @@
 		}
 		else
 		{
-			QMessageBox::critical(this, "Problem opening file",
-				QString{"Could not open %1: %2"}
+			QMessageBox::critical(this, tr("Problem opening file"),
+				tr("Could not open %1: %2")
 					.arg(path)
 					.arg(file.errorString()));
 		}
@@ -50,6 +53,51 @@
 }
 
 /**
+ * @brief Changes the application language to the specified language
+ * @param localeCode Code of the locale to translate to
+ */
+void MainWindow::changeLanguage(const QString& localeCode)
+{
+	if (not localeCode.isEmpty() and localeCode != this->currentLanguage)
+	{
+		this->currentLanguage = localeCode;
+		QLocale::setDefault({localeCode});
+		qApp->removeTranslator(&this->translator);
+		const bool loadSuccessful = this->translator.load(pathToTranslation(localeCode));
+		if (loadSuccessful)
+		{
+			qApp->installTranslator(&this->translator);
+		}
+	}
+}
+
+void MainWindow::languageChangeRequested()
+{
+	QAction* const senderAction = qobject_cast<QAction*>(sender());
+	if (senderAction != nullptr)
+	{
+		const QString localeCode = senderAction->data().toString();
+		this->changeLanguage(localeCode);
+	}
+}
+
+void MainWindow::changeEvent(QEvent* event)
+{
+	if (event != nullptr)
+	{
+		switch (event->type())
+		{
+		case QEvent::LanguageChange:
+			this->ui->retranslateUi(this);
+			break;
+		default:
+			break;
+		}
+	}
+	QMainWindow::changeEvent(event);
+}
+
+/**
  * @brief Creates a new tab widget for the specified model.
  * @param model Model to get a new tab widget for
  * @return widget
@@ -105,3 +153,31 @@
 	title += fullVersionString();
 	setWindowTitle(title);
 }
+
+QString MainWindow::pathToTranslation(const QString& localeCode)
+{
+	QDir dir {":/locale"};
+	return dir.filePath(localeCode + ".qm");
+}
+
+void MainWindow::loadLocales()
+{
+	QDir dir {":/locale"};
+	this->ui->menuLanguage->clear();
+	QVector<QString> localeCodes = {"en"};
+	for (const QFileInfo& file : dir.entryInfoList(QDir::Files))
+	{
+		localeCodes.append(file.baseName());
+	}
+	for (const QString& localeCode : localeCodes)
+	{
+		const QLocale locale{localeCode};
+		const QString languageName = QLocale::languageToString(locale.language());
+		const QIcon flag{":/flags/" + localeCode + ".png"};
+		QAction* action = new QAction{languageName, this};
+		action->setData(localeCode);
+		action->setIcon(flag);
+		this->ui->menuLanguage->addAction(action);
+		connect(action, &QAction::triggered, this, &MainWindow::languageChangeRequested);
+	}
+}
--- a/src/mainwindow.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/mainwindow.h	Thu Oct 03 23:44:28 2019 +0300
@@ -1,5 +1,6 @@
 #pragma once
 #include <QMainWindow>
+#include <QTranslator>
 #include <memory>
 #include <vector>
 #include "documentmanager.h"
@@ -13,12 +14,20 @@
 private slots:
 	void newModel();
 	void openModel();
+	void languageChangeRequested();
+protected:
+	void changeEvent(QEvent* event);
 private:
 	std::unique_ptr<class Ui_MainWindow> ui;
 	DocumentManager documents;
 	QMap<Model*, QWidget*> modelWidgets;
 	QWidget* createWidgetForModel(Model* model);
 	QWidget* getWidgetForModel(Model* model);
+	void loadLocales();
 	void updateTabs();
 	void updateTitle();
+	void changeLanguage(const QString& localeCode);
+	static QString pathToTranslation(const QString& localeCode);
+	QString currentLanguage = "en";
+	QTranslator translator;
 };
--- a/src/mainwindow.ui	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/mainwindow.ui	Thu Oct 03 23:44:28 2019 +0300
@@ -38,7 +38,13 @@
     <addaction name="separator"/>
     <addaction name="actionQuit"/>
    </widget>
+   <widget class="QMenu" name="menuLanguage">
+    <property name="title">
+     <string>Language</string>
+    </property>
+   </widget>
    <addaction name="menuFile"/>
+   <addaction name="menuLanguage"/>
   </widget>
   <widget class="QStatusBar" name="statusbar"/>
   <action name="actionQuit">
--- a/src/model.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/model.h	Thu Oct 03 23:44:28 2019 +0300
@@ -13,6 +13,8 @@
 	Model(const Model&) = delete;
 	int size() const;
 	EditContext edit();
+signals:
+	void objectAdded(modelobjects::Id id, int position);
 private:
 	template<typename T, typename... Args>
 	T* append(Args&&... args);
@@ -23,18 +25,25 @@
 	using ModelObjectPointer = std::unique_ptr<modelobjects::BaseObject>;
 	LDHeader header;
 	std::vector<ModelObjectPointer> body;
+	std::map<modelobjects::Id, modelobjects::BaseObject*> objectsById;
 };
 
 template<typename T, typename... Args>
 T* Model::append(Args&&... args)
 {
 	this->body.push_back(std::make_unique<T>(args...));
-	return static_cast<T*>(this->body.back().get());
+	T* pointer = static_cast<T*>(this->body.back().get());
+	this->objectsById[pointer->id] = pointer;
+	emit objectAdded(pointer->id, this->body.size() - 1);
+	return pointer;
 }
 
 template<typename T, typename... Args>
 T* Model::insert(int position, Args&&... args)
 {
 	this->body.insert(position, std::make_unique<T>(args...));
-	return this->body[position];
+	T* pointer = static_cast<T*>(this->body[position]);
+	this->objectsById[pointer->id] = pointer;
+	emit objectAdded(pointer->id, position);
+	return pointer;
 }
--- a/src/objecttypes/comment.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/comment.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -1,3 +1,4 @@
+#include <QFont>
 #include "comment.h"
 
 modelobjects::Comment::Comment(QStringView text) :
@@ -27,3 +28,15 @@
 		return BaseObject::setProperty(property, value);
 	}
 }
+
+QString modelobjects::Comment::textRepresentation() const
+{
+	return this->storedText;
+}
+
+QFont modelobjects::Comment::textRepresentationFont() const
+{
+	QFont font;
+	font.setItalic(true);
+	return font;
+}
--- a/src/objecttypes/comment.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/comment.h	Thu Oct 03 23:44:28 2019 +0300
@@ -15,6 +15,8 @@
 	SetPropertyResult setProperty(
 		Property property,
 		const QVariant& value) override;
+	QString textRepresentation() const override;
+	QFont textRepresentationFont() const override;
 private:
 	QString storedText = "";
 };
--- a/src/objecttypes/conditionaledge.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/conditionaledge.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -40,3 +40,10 @@
 		return Edge::setProperty(property, value);
 	}
 }
+
+QString modelobjects::ConditionalEdge::textRepresentation() const
+{
+	return Edge::textRepresentation() + format("%1 %2",
+		vertexToStringParens(controlPoint_1),
+		vertexToStringParens(controlPoint_2));
+}
--- a/src/objecttypes/conditionaledge.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/conditionaledge.h	Thu Oct 03 23:44:28 2019 +0300
@@ -20,6 +20,7 @@
 	SetPropertyResult setProperty(
 		Property property,
 		const QVariant& value) override;
+	QString textRepresentation() const override;
 private:
 	Vertex controlPoint_1 = {};
 	Vertex controlPoint_2 = {};
--- a/src/objecttypes/edge.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/edge.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -36,3 +36,8 @@
 		return BaseClass::setProperty(property, value);
 	}
 }
+
+QString modelobjects::Edge::textRepresentation() const
+{
+	return format("%1 %2", vertexToStringParens(point_1), vertexToStringParens(point_2));
+}
--- a/src/objecttypes/edge.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/edge.h	Thu Oct 03 23:44:28 2019 +0300
@@ -17,6 +17,7 @@
 	SetPropertyResult setProperty(
 		Property property,
 		const QVariant& value) override;
+	QString textRepresentation() const override;
 private:
 	Vertex point_1 = {};
 	Vertex point_2 = {};
--- a/src/objecttypes/errorline.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/errorline.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -1,3 +1,4 @@
+#include <QBrush>
 #include "errorline.h"
 
 modelobjects::ErrorLine::ErrorLine(QStringView text) :
@@ -30,3 +31,18 @@
 		return BaseObject::setProperty(property, value);
 	}
 }
+
+QString modelobjects::ErrorLine::textRepresentation() const
+{
+	return this->text;
+}
+
+QBrush modelobjects::ErrorLine::textRepresentationForeground() const
+{
+	return QBrush{Qt::yellow};
+}
+
+QBrush modelobjects::ErrorLine::textRepresentationBackground() const
+{
+	return QBrush{Qt::red};
+}
--- a/src/objecttypes/errorline.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/errorline.h	Thu Oct 03 23:44:28 2019 +0300
@@ -14,6 +14,9 @@
 	SetPropertyResult setProperty(
 		Property property,
 		const QVariant& value) override;
+	QString textRepresentation() const override;
+	QBrush textRepresentationForeground() const override;
+	QBrush textRepresentationBackground() const override;
 private:
 	QString text;
 };
--- a/src/objecttypes/modelobject.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/modelobject.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -1,14 +1,25 @@
+#include <QBrush>
+#include <QFont>
 #include "modelobject.h"
 
+/*
 static Uuid &getUuidForNewObject()
 {
     static Uuid running_uuid {0, 0};
     incrementUuid(running_uuid);
     return running_uuid;
 }
+*/
+
+static unsigned int getIdForNewObject()
+{
+	static unsigned int id = 0;
+	id += 1;
+	return id;
+}
 
 modelobjects::BaseObject::BaseObject() :
-    id {getUuidForNewObject()}
+	id {getIdForNewObject()}
 {
 }
 
@@ -35,6 +46,21 @@
 	return SetPropertyResult::PropertyNotHandled;
 }
 
+QBrush modelobjects::BaseObject::textRepresentationForeground() const
+{
+	return {};
+}
+
+QBrush modelobjects::BaseObject::textRepresentationBackground() const
+{
+	return {};
+}
+
+QFont modelobjects::BaseObject::textRepresentationFont() const
+{
+	return {};
+}
+
 modelobjects::ColoredBaseObject::ColoredBaseObject(const Color color_index) :
 	color_index{color_index}
 {
--- a/src/objecttypes/modelobject.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/modelobject.h	Thu Oct 03 23:44:28 2019 +0300
@@ -4,7 +4,6 @@
 #include <QStringView>
 #include "main.h"
 #include "colors.h"
-#include "uuid.h"
 #include "vertex.h"
 
 namespace modelobjects
@@ -42,11 +41,15 @@
 	BaseObject();
 	BaseObject(const BaseObject&) = delete;
 	virtual ~BaseObject();
-	const Uuid id;
+	const unsigned int id;
 	//virtual void toString(QTextStream &out) = 0;
 	virtual bool hasColor() const;
 	virtual QVariant getProperty(Property id) const;
 	virtual SetPropertyResult setProperty(Property id, const QVariant& value);
+	virtual QString textRepresentation() const = 0;
+	virtual QBrush textRepresentationForeground() const;
+	virtual QBrush textRepresentationBackground() const;
+	virtual QFont textRepresentationFont() const;
 };
 
 class modelobjects::ColoredBaseObject : public BaseObject
@@ -59,3 +62,8 @@
 private:
 	Color color_index = colors::main;
 };
+
+namespace modelobjects
+{
+	using Id = std::remove_const_t<decltype(BaseObject::id)>;
+}
--- a/src/objecttypes/polygon.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/polygon.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -44,6 +44,14 @@
 	}
 }
 
+QString modelobjects::Triangle::textRepresentation() const
+{
+	return format("%1 %2 %3",
+		vertexToStringParens(points[0]),
+		vertexToStringParens(points[1]),
+		vertexToStringParens(points[2]));
+}
+
 modelobjects::Quadrilateral::Quadrilateral(
 	const Vertex& point_1,
 	const Vertex& point_2,
@@ -95,3 +103,12 @@
 		return ColoredBaseObject::setProperty(id, value);
 	}
 }
+
+QString modelobjects::Quadrilateral::textRepresentation() const
+{
+	return format("%1 %2 %3 %4",
+		vertexToStringParens(points[0]),
+		vertexToStringParens(points[1]),
+		vertexToStringParens(points[2]),
+		vertexToStringParens(points[3]));
+}
--- a/src/objecttypes/polygon.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/polygon.h	Thu Oct 03 23:44:28 2019 +0300
@@ -18,6 +18,7 @@
 		Color color_index = colors::main);
 	QVariant getProperty(Property id) const override;
 	SetPropertyResult setProperty(Property id, const QVariant& value) override;
+	QString textRepresentation() const override;
 private:
 	Vertex points[3] = {{}};
 };
@@ -34,6 +35,7 @@
 		Color color_index = colors::main);
 	QVariant getProperty(Property id) const override;
 	SetPropertyResult setProperty(Property id, const QVariant& value) override;
+	QString textRepresentation() const override;
 private:
 	Vertex points[4] = {{}};
 };
--- a/src/objecttypes/subfilereference.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/subfilereference.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -14,3 +14,8 @@
 		return ColoredBaseObject::getProperty(property);
 	}
 }
+
+QString modelobjects::SubfileReference::textRepresentation() const
+{
+	return referenceName + " " + vertexToStringParens(this->position);
+}
--- a/src/objecttypes/subfilereference.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/objecttypes/subfilereference.h	Thu Oct 03 23:44:28 2019 +0300
@@ -11,6 +11,7 @@
 public:
 	SubfileReference() = default;
 	QVariant getProperty(Property property) const override;
+	QString textRepresentation() const override;
 private:
 	Vertex position;
 	QMatrix3x3 transformation;
--- a/src/uuid.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-#include "uuid.h"
-#include <QIODevice>
-
-void uuidToString(const Uuid &uuid, QTextStream &stream)
-{
-    stream << "0x";
-    stream << QString::number(uuid.a, 16);
-    stream << QString::number(uuid.b, 16);
-}
-
-void incrementUuid(Uuid &uuid)
-{
-    if (uuid.b == std::numeric_limits<decltype(uuid.b)>::max())
-    {
-        uuid.a += 1;
-        uuid.b = 0;
-    }
-    else
-    {
-        uuid.b += 1;
-    }
-}
--- a/src/uuid.h	Thu Oct 03 11:45:44 2019 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-#pragma once
-#include <QString>
-#include <QTextStream>
-
-struct Uuid
-{
-    quint64 a;
-    quint64 b;
-};
-
-void incrementUuid(Uuid &uuid);
-void uuidToString(const Uuid &uuid, QTextStream &stream);
--- a/src/vertex.cpp	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/vertex.cpp	Thu Oct 03 23:44:28 2019 +0300
@@ -213,3 +213,8 @@
 {
 	return qHash(key.x) ^ rotl10(qHash(key.y)) ^ rotl20(qHash(key.z));
 }
+
+QString vertexToStringParens(const Vertex& vertex)
+{
+	return format("(%1, %2, %3)", vertex.x, vertex.y, vertex.z);
+}
--- a/src/vertex.h	Thu Oct 03 11:45:44 2019 +0300
+++ b/src/vertex.h	Thu Oct 03 23:44:28 2019 +0300
@@ -54,6 +54,7 @@
 qreal distance(const Vertex& one, const Vertex& other);
 Vertex vertexFromVector(const QVector3D& vector);
 QVector3D vertexToVector(const Vertex &vertex);
+QString vertexToStringParens(const Vertex& vertex);
 unsigned int qHash(const Vertex& key);
 Vertex operator-(const Vertex& vertex);
 QDataStream& operator<<(QDataStream& out, const Vertex& vertex);

mercurial