Added basic header editing

Fri, 16 Mar 2018 16:28:39 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Fri, 16 Mar 2018 16:28:39 +0200
changeset 1291
9c570a30c98a
parent 1290
8db26042f3d1
child 1292
66d2050d3bd9

Added basic header editing

CMakeLists.txt file | annotate | diff | comparison | revisions
src/basics.h file | annotate | diff | comparison | revisions
src/documentmanager.cpp file | annotate | diff | comparison | revisions
src/headerhistorymodel.cpp file | annotate | diff | comparison | revisions
src/headerhistorymodel.h file | annotate | diff | comparison | revisions
src/lddocument.cpp file | annotate | diff | comparison | revisions
src/lddocument.h 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/parser.cpp file | annotate | diff | comparison | revisions
src/widgets/headeredit.cpp file | annotate | diff | comparison | revisions
src/widgets/headeredit.h file | annotate | diff | comparison | revisions
src/widgets/headeredit.ui file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Fri Mar 16 12:20:16 2018 +0200
+++ b/CMakeLists.txt	Fri Mar 16 16:28:39 2018 +0200
@@ -39,6 +39,7 @@
 	src/glrenderer.cpp
 	src/grid.cpp
 	src/guiutilities.cpp
+	src/headerhistorymodel.cpp
 	src/hierarchyelement.cpp
 	src/lddocument.cpp
 	src/ldpaths.cpp
@@ -87,6 +88,7 @@
 	src/toolsets/toolset.cpp
 	src/toolsets/viewtoolset.cpp
 	src/types/matrix.cpp
+	src/widgets/headeredit.cpp
 )
 
 set (LDFORGE_HEADERS
@@ -104,6 +106,7 @@
 	src/glShared.h
 	src/grid.h
 	src/guiutilities.h
+	src/headerhistorymodel.h
 	src/hierarchyelement.h
 	src/lddocument.h
 	src/ldobjectiterator.h
@@ -157,6 +160,7 @@
 	src/toolsets/toolset.h
 	src/toolsets/viewtoolset.h
 	src/types/matrix.h
+	src/widgets/headeredit.h
 )
 
 set (LDFORGE_FORMS
@@ -181,6 +185,7 @@
 	src/dialogs/ytruderdialog.ui
 	src/mainwindow.ui
 	src/partdownloader.ui
+	src/widgets/headeredit.ui
 )
 
 set (LDFORGE_OTHER_FILES
@@ -189,11 +194,9 @@
 )
 
 set (LDFORGE_RESOURCES ldforge.qrc)
-# set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lGLU")
-
-if (NOT MSVC)
-	set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -W -Wall")
-endif()
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
 
 if (TRANSPARENT_DIRECT_COLORS)
 	set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTRANSPARENT_DIRECT_COLORS")
@@ -210,6 +213,7 @@
 include_directories ("${PROJECT_BINARY_DIR}")
 include_directories ("${PROJECT_BINARY_DIR}/src")
 include_directories ("${PROJECT_BINARY_DIR}/src/misc")
+include_directories ("${PROJECT_SOURCE_DIR}/src")
 
 if (NOT MSVC)
 	if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
--- a/src/basics.h	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/basics.h	Fri Mar 16 16:28:39 2018 +0200
@@ -391,3 +391,15 @@
 {
 	return min(a, min(rest...));
 }
+
+/*
+ * Assigns the value of a single flag in a flagset
+ */
+template<int Flag, typename T>
+void assignFlag(QFlags<T>& flagset, bool value)
+{
+	if (value)
+		flagset |= static_cast<T>(Flag);
+	else
+		flagset &= ~static_cast<T>(Flag);
+}
--- a/src/documentmanager.cpp	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/documentmanager.cpp	Fri Mar 16 16:28:39 2018 +0200
@@ -309,7 +309,7 @@
 
 	int numWarnings;
 	Parser parser {*fp};
-	load->setHeader(parser.parseHeader());
+	load->header = parser.parseHeader();
 	parser.parseBody(*load);
 	fp->close();
 	fp->deleteLater();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/headerhistorymodel.cpp	Fri Mar 16 16:28:39 2018 +0200
@@ -0,0 +1,75 @@
+#include "headerhistorymodel.h"
+#include "lddocument.h"
+
+HeaderHistoryModel::HeaderHistoryModel(LDHeader* header, QObject* parent) :
+	QAbstractTableModel {parent},
+	header {header} {}
+
+int HeaderHistoryModel::rowCount(const QModelIndex&) const
+{
+	if (this->header)
+		return this->header->history.size();
+}
+
+int HeaderHistoryModel::columnCount(const QModelIndex&) const
+{
+	return 3;
+}
+
+QVariant HeaderHistoryModel::data(const QModelIndex& index, int role) const
+{
+	if (this->header and role == Qt::DisplayRole)
+	{
+		const auto& entry = this->header->history[index.row()];
+		switch (static_cast<Column>(index.column()))
+		{
+		case DateColumn:
+			return entry.date;
+
+		case AuthorColumn:
+			return entry.author;
+
+		case DescriptionColumn:
+			return entry.description;
+
+		default:
+			return {};
+		}
+	}
+	else
+	{
+		return {};
+	}
+}
+
+QVariant HeaderHistoryModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+	if (orientation == Qt::Horizontal and role == Qt::DisplayRole)
+	{
+		switch (static_cast<Column>(section))
+		{
+		case DateColumn:
+			return tr("Date");
+
+		case AuthorColumn:
+			return tr("Author");
+
+		case DescriptionColumn:
+			return tr("Description");
+
+		default:
+			return {};
+		}
+	}
+	else
+	{
+		return {};
+	}
+}
+
+void HeaderHistoryModel::setHeader(LDHeader* header)
+{
+	emit layoutAboutToBeChanged();
+	this->header = header;
+	emit layoutChanged();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/headerhistorymodel.h	Fri Mar 16 16:28:39 2018 +0200
@@ -0,0 +1,26 @@
+#pragma once
+#include <QAbstractTableModel>
+
+class LDHeader;
+
+class HeaderHistoryModel : public QAbstractTableModel
+{
+public:
+	enum Column
+	{
+		DateColumn,
+		AuthorColumn,
+		DescriptionColumn,
+	};
+
+	HeaderHistoryModel(LDHeader* header, QObject* parent);
+
+	int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+	int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+	QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+	void setHeader(LDHeader* header);
+	QVariant headerData(int section, Qt::Orientation orientation, int role) const;
+
+private:
+	LDHeader* header;
+};
--- a/src/lddocument.cpp	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/lddocument.cpp	Fri Mar 16 16:28:39 2018 +0200
@@ -69,11 +69,6 @@
 	return m_name;
 }
 
-void LDDocument::setHeader(LDHeader&& header)
-{
-	this->m_header = header;
-}
-
 void LDDocument::setName (QString value)
 {
 	m_name = value;
@@ -454,17 +449,13 @@
 }
 
 /*
- * Special operator definition that implements the XOR operator for the winding.
+ * Special operator definition that implements the XOR operator for windings.
  * However, if either winding is NoWinding, then this function returns NoWinding.
  */
-decltype(LDHeader::winding) operator^(
-	decltype(LDHeader::winding) one,
-	decltype(LDHeader::winding) other
-) {
-	if (one == LDHeader::NoWinding or other == LDHeader::NoWinding)
-		return LDHeader::NoWinding;
-	else if (one != other)
-		return LDHeader::Clockwise;
+Winding operator^(Winding one, Winding other)
+{
+	if (one == NoWinding or other == NoWinding)
+		return NoWinding;
 	else
-		return LDHeader::CounterClockwise;
+		return static_cast<Winding>(static_cast<int>(one) ^ static_cast<int>(other));
 }
--- a/src/lddocument.h	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/lddocument.h	Fri Mar 16 16:28:39 2018 +0200
@@ -28,6 +28,13 @@
 struct LDGLData;
 class DocumentManager;
 
+enum Winding
+{
+	NoWinding,
+	CounterClockwise,
+	Clockwise,
+};
+
 struct LDHeader
 {
 	struct HistoryEntry
@@ -35,10 +42,10 @@
 		QDate date;
 		QString author;
 		QString description;
-		enum { UserName, RealName } authorType = UserName;
 	};
 	enum FileType
 	{
+		NoHeader,
 		Part,
 		Subpart,
 		Shortcut,
@@ -46,7 +53,7 @@
 		Primitive_8,
 		Primitive_48,
 		Configuration,
-	} type = Part;
+	} type = NoHeader;
 	enum Qualifier
 	{
 		Alias = 1 << 0,
@@ -56,35 +63,23 @@
 	QFlags<Qualifier> qualfiers;
 	QString description;
 	QString name;
-	struct
-	{
-		QString realName;
-		QString userName;
-	} author;
+	QString author;
 	QString category;
 	QString cmdline;
-	QStringList help;
-	QStringList keywords;
+	QString help;
+	QString keywords;
 	QVector<HistoryEntry> history;
+	Winding winding = NoWinding;
 	enum
 	{
-		NoWinding,
-		CounterClockwise,
-		Clockwise,
-	} winding = NoWinding;
-	enum
-	{
+		Unspecified,
 		CaLicense,
 		NonCaLicense
-	} license = CaLicense;
+	} license = Unspecified;
 };
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags<LDHeader::Qualifier>)
-
-decltype(LDHeader::winding) operator^(
-	decltype(LDHeader::winding) one,
-	decltype(LDHeader::winding) other
-);
+Winding operator^(Winding one, Winding other);
 
 //
 // This class stores a document either as a editable file for the user or for
@@ -101,6 +96,8 @@
 	Q_OBJECT
 
 public:
+	LDHeader header;
+
 	LDDocument (DocumentManager* parent);
 	~LDDocument();
 
@@ -129,7 +126,6 @@
 	void setDefaultName (QString value);
 	void setFrozen(bool value);
 	void setFullPath (QString value);
-	void setHeader(LDHeader&& header);
 	void setName (QString value);
 	void setSavePosition (long value);
 	void setTabIndex (int value);
@@ -143,7 +139,6 @@
 	LDObject* withdrawAt(int position);
 
 private:
-	LDHeader m_header;
 	QString m_name;
 	QString m_fullPath;
 	QString m_defaultName;
--- a/src/mainwindow.cpp	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/mainwindow.cpp	Fri Mar 16 16:28:39 2018 +0200
@@ -86,6 +86,12 @@
 		connect (act, SIGNAL (triggered()), this, SLOT (actionTriggered()));
 		m_defaultShortcuts[act] = act->shortcut();
 	});
+	connect(
+		ui.header,
+		&HeaderEdit::descriptionChanged,
+		this,
+		&MainWindow::updateTitle
+	);
 
 	updateGridToolBar();
 	updateEditModeActions();
@@ -277,7 +283,7 @@
 //
 void MainWindow::updateTitle()
 {
-	QString title = format (APPNAME " " VERSION_STRING);
+	QString title = APPNAME " " VERSION_STRING;
 
 	// Append our current file if we have one
 	if (m_currentDocument)
@@ -285,13 +291,8 @@
 		title += ": ";
 		title += m_currentDocument->getDisplayName();
 
-		if (m_currentDocument->size() > 0 and
-		    m_currentDocument->getObject(0)->type() == LDObjectType::Comment)
-		{
-			// Append title
-			LDComment* comm = static_cast <LDComment*> (m_currentDocument->getObject (0));
-			title += format (": %1", comm->text());
-		}
+		if (not m_currentDocument->header.description.isEmpty())
+			title += format (": %1", m_currentDocument->header.description);
 
 		if (m_currentDocument->hasUnsavedChanges())
 			title += '*';
@@ -947,6 +948,7 @@
 		updateTitle();
 		print ("Changed document to %1", document->getDisplayName());
 		ui.objectList->setModel(document);
+		ui.header->setHeader(&document->header);
 		QItemSelectionModel* selection = m_selections.value(document);
 
 		if (selection == nullptr)
--- a/src/mainwindow.h	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/mainwindow.h	Fri Mar 16 16:28:39 2018 +0200
@@ -114,7 +114,6 @@
 	void updateEditModeActions();
 	void updateGridToolBar();
 	void updateRecentFilesMenu();
-	void updateTitle();
 
 	static QPixmap getIcon(QString iconName);
 	static class QSettings* makeSettings(QObject* parent = nullptr);
@@ -137,6 +136,7 @@
 	void ringToolHiResClicked (bool clicked);
 	void tabSelected();
 	void documentClosed(LDDocument* document);
+	void updateTitle();
 
 protected:
 	void closeEvent (QCloseEvent* ev);
--- a/src/mainwindow.ui	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/mainwindow.ui	Fri Mar 16 16:28:39 2018 +0200
@@ -42,13 +42,23 @@
         <property name="currentIndex">
          <number>0</number>
         </property>
+        <widget class="QWidget" name="page">
+         <attribute name="label">
+          <string>Header</string>
+         </attribute>
+         <layout class="QVBoxLayout" name="verticalLayout_6">
+          <item>
+           <widget class="HeaderEdit" name="header" native="true"/>
+          </item>
+         </layout>
+        </widget>
         <widget class="QWidget" name="pageDocument">
          <property name="geometry">
           <rect>
            <x>0</x>
            <y>0</y>
            <width>465</width>
-           <height>399</height>
+           <height>362</height>
           </rect>
          </property>
          <attribute name="label">
@@ -76,7 +86,7 @@
            <x>0</x>
            <y>0</y>
            <width>465</width>
-           <height>399</height>
+           <height>362</height>
           </rect>
          </property>
          <attribute name="label">
@@ -158,7 +168,7 @@
            <x>0</x>
            <y>0</y>
            <width>465</width>
-           <height>399</height>
+           <height>362</height>
           </rect>
          </property>
          <attribute name="label">
@@ -1723,6 +1733,14 @@
    </property>
   </action>
  </widget>
+ <customwidgets>
+  <customwidget>
+   <class>HeaderEdit</class>
+   <extends>QWidget</extends>
+   <header>widgets/headeredit.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
  <resources>
   <include location="../ldforge.qrc"/>
   <include location="../ldforge.qrc"/>
--- a/src/parser.cpp	Fri Mar 16 12:20:16 2018 +0200
+++ b/src/parser.cpp	Fri Mar 16 16:28:39 2018 +0200
@@ -37,7 +37,7 @@
  */
 QString Parser::readLine()
 {
-	return QString::fromUtf8(this->device.readLine()).simplified();
+	return QString::fromUtf8(this->device.readLine()).trimmed();
 }
 
 /*
@@ -97,17 +97,17 @@
 	}
 	else if (line == "0 BFC CERTIFY CCW")
 	{
-		header.winding = LDHeader::CounterClockwise;
+		header.winding = CounterClockwise;
 		return ParseSuccess;
 	}
 	else if (line == "0 BFC CERTIFY CW")
 	{
-		header.winding = LDHeader::Clockwise;
+		header.winding = Clockwise;
 		return ParseSuccess;
 	}
 	else if (line == "0 BFC NOCERTIFY")
 	{
-		header.winding = LDHeader::NoWinding;
+		header.winding = NoWinding;
 		return ParseSuccess;
 	}
 	else if (line.startsWith("0 !HISTORY "))
@@ -118,18 +118,17 @@
 		};
 		if (historyRegexp.exactMatch(line))
 		{
-			QString dateString = historyRegexp.capturedTexts().value(0);
-			QString authorWithPrefix = historyRegexp.capturedTexts().value(1);
-			QString description = historyRegexp.capturedTexts().value(2);
+			QString dateString = historyRegexp.capturedTexts().value(1);
+			QString authorWithPrefix = historyRegexp.capturedTexts().value(2);
+			QString description = historyRegexp.capturedTexts().value(3);
 			LDHeader::HistoryEntry historyEntry;
 			historyEntry.date = QDate::fromString(dateString, Qt::ISODate);
-			historyEntry.author = authorWithPrefix.mid(1);
 			historyEntry.description = description;
 
 			if (authorWithPrefix[0] == '{')
-				historyEntry.authorType = LDHeader::HistoryEntry::RealName;
+				historyEntry.author = authorWithPrefix + "}";
 			else
-				historyEntry.authorType = LDHeader::HistoryEntry::UserName;
+				historyEntry.author = authorWithPrefix.mid(1);
 
 			header.history.append(historyEntry);
 			return ParseSuccess;
@@ -141,18 +140,8 @@
 	}
 	else if (line.startsWith("0 Author: "))
 	{
-		static const QRegExp authorRegexp {R"(0 Author: ([^[]+)(?: \[([^]]+)\])?)"};
-		if (authorRegexp.exactMatch(line))
-		{
-			QStringList tokens = authorRegexp.capturedTexts();
-			header.author.realName = tokens.value(0);
-			header.author.userName = tokens.value(1);
-			return ParseSuccess;
-		}
-		else
-		{
-			return ParseFailure;
-		}
+		header.author = line.mid(strlen("0 Author: "));
+		return ParseSuccess;
 	}
 	else if (line.startsWith("0 Name: "))
 	{
@@ -161,12 +150,16 @@
 	}
 	else if (line.startsWith("0 !HELP "))
 	{
-		header.help.append(line.mid(strlen("0 !HELP ")));
+		if (not header.help.isEmpty())
+			header.help += "\n";
+		header.help += line.mid(strlen("0 !HELP "));
 		return ParseSuccess;
 	}
 	else if (line.startsWith("0 !KEYWORDS "))
 	{
-		header.keywords.append(line.mid(strlen("0 !KEYWORDS ")));
+		if (not header.keywords.isEmpty())
+			header.keywords += "\n";
+		header.keywords += line.mid(strlen("0 !KEYWORDS "));
 		return ParseSuccess;
 	}
 	else if (line.startsWith("0 !CATEGORY "))
@@ -209,7 +202,7 @@
 		QString descriptionLine = this->readLine();
 		if (descriptionLine.startsWith("0 "))
 		{
-			header.description = descriptionLine.mid(strlen("0 ")).simplified();
+			header.description = descriptionLine.mid(strlen("0 ")).trimmed();
 
 			// Parse the rest of the header
 			while (not this->device.atEnd())
@@ -356,7 +349,7 @@
 			{
 				// Comment
 				QString commentText = line.mid(line.indexOf("0") + 2);
-				QString commentTextSimplified = commentText.simplified();
+				QString commentTextSimplified = commentText.trimmed();
 
 				// Handle BFC statements
 				if (countof(tokens) > 2 and tokens[1] == "BFC")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/headeredit.cpp	Fri Mar 16 16:28:39 2018 +0200
@@ -0,0 +1,155 @@
+#include "headeredit.h"
+#include "ui_headeredit.h"
+#include "../lddocument.h"
+#include "../headerhistorymodel.h"
+
+static const QStringList categories {
+	"",
+	"Animal", "Antenna", "Arch", "Arm", "Bar", "Baseplate", "Belville", "Boat", "Bracket",
+	"Brick", "Canvas", "Car", "Clikits", "Cockpit", "Cone", "Constraction",
+	"Constraction Accessory", "Container", "Conveyor", "Crane", "Cylinder", "Dish", "Door",
+	"Electric", "Exhaust", "Fence", "Figure", "Figure Accessory", "Flag", "Forklift", "Freestyle",
+	"Garage", "Glass", "Grab", "Hinge", "Homemaker", "Hose", "Ladder", "Lever", "Magnet", "Minifig",
+	"Minifig Accessory", "Minifig Footwear", "Minifig Headwear", "Minifig Hipwear",
+	"Minifig Neckwear", "Monorail", "Panel", "Plane", "Plant", "Plate", "Platform", "Propellor",
+	"Rack", "Roadsign", "Rock", "Scala", "Screw", "Sheet", "Slope", "Sphere", "Staircase",
+	"Sticker", "Support", "Tail", "Tap", "Technic", "Tile", "Tipper", "Tractor", "Trailer",
+	"Train", "Turntable", "Tyre", "Vehicle", "Wedge", "Wheel", "Winch", "Window", "Windscreen",
+	"Wing", "Znap",
+};
+
+HeaderEdit::HeaderEdit(QWidget* parent) :
+	QWidget {parent},
+	ui {*new Ui_HeaderEdit},
+	headerHistoryModel {new HeaderHistoryModel {nullptr, this}}
+{
+	ui.setupUi(this);
+
+	this->ui.category->addItems(::categories);
+	this->ui.category->setItemText(0, "(unspecified)");
+	this->ui.history->setModel(this->headerHistoryModel);
+
+	connect(
+		ui.description,
+		&QLineEdit::textChanged,
+		[&](const QString& text)
+		{
+			if (this->hasValidHeader())
+			{
+				this->m_header->description = text;
+				emit descriptionChanged(text);
+			}
+		}
+	);
+	connect(
+		ui.author,
+		&QLineEdit::textChanged,
+		[&](const QString& text)
+		{
+			if (this->hasValidHeader())
+				this->m_header->author = text;
+		}
+	);
+	connect(
+		ui.winding,
+		qOverload<int>(&QComboBox::currentIndexChanged),
+		[&](int index)
+		{
+			if (this->hasValidHeader())
+			{
+				this->m_header->winding = static_cast<Winding>(index);
+				emit windingChanged(this->m_header->winding);
+			}
+		}
+	);
+	connect(
+		ui.license,
+		qOverload<int>(&QComboBox::currentIndexChanged),
+		[&](int index)
+		{
+			if (this->m_header)
+				this->m_header->license = static_cast<decltype(LDHeader::license)>(index);
+		}
+	);
+	connect(
+		ui.category,
+		qOverload<int>(&QComboBox::currentIndexChanged),
+		[&](int index)
+		{
+			if (this->hasValidHeader())
+				this->m_header->category = ::categories.value(index);
+		}
+	);
+	connect(
+		ui.alias,
+		&QCheckBox::stateChanged,
+		[&](int state)
+		{
+			if (this->hasValidHeader())
+				assignFlag<LDHeader::Alias>(this->m_header->qualfiers, state == Qt::Checked);
+		}
+	);
+	connect(
+		ui.physicalColor,
+		&QCheckBox::stateChanged,
+		[&](int state)
+		{
+			if (this->hasValidHeader())
+			{
+				assignFlag<LDHeader::Physical_Color>(
+					this->m_header->qualfiers,
+					state == Qt::Checked
+				);
+			}
+		}
+	);
+	connect(
+		ui.flexibleSection,
+		&QCheckBox::stateChanged,
+		[&](int state)
+		{
+			if (this->hasValidHeader())
+			{
+				assignFlag<LDHeader::Flexible_Section>(
+					this->m_header->qualfiers,
+					state == Qt::Checked
+				);
+			}
+		}
+	);
+	this->setEnabled(this->hasValidHeader());
+}
+
+HeaderEdit::~HeaderEdit()
+{
+	delete this->headerHistoryModel;
+	delete &this->ui;
+}
+
+void HeaderEdit::setHeader(LDHeader* header)
+{
+	this->m_header = header;
+	this->ui.description->setText(header->description);
+	this->ui.author->setText(header->author);
+	this->ui.category->setCurrentIndex(::categories.indexOf(header->category));
+	this->ui.license->setCurrentIndex(static_cast<int>(header->license));
+	this->ui.alias->setChecked(header->qualfiers & LDHeader::Alias);
+	this->ui.physicalColor->setChecked(header->qualfiers & LDHeader::Physical_Color);
+	this->ui.flexibleSection->setChecked(header->qualfiers & LDHeader::Flexible_Section);
+	this->ui.cmdline->setText(header->cmdline);
+	this->ui.winding->setCurrentIndex(header->winding);
+	this->ui.keywords->document()->setPlainText(header->keywords);
+	this->ui.help->document()->setPlainText(header->help);
+	this->headerHistoryModel->setHeader(header);
+	this->setEnabled(this->hasValidHeader());
+}
+
+LDHeader* HeaderEdit::header() const
+{
+	return this->m_header;
+}
+
+bool HeaderEdit::hasValidHeader() const
+{
+	return this->m_header != nullptr and this->m_header->type != LDHeader::NoHeader;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/headeredit.h	Fri Mar 16 16:28:39 2018 +0200
@@ -0,0 +1,25 @@
+#pragma once
+#include <QWidget>
+#include "../lddocument.h"
+
+class HeaderEdit : public QWidget
+{
+	Q_OBJECT
+
+public:
+	HeaderEdit(QWidget* parent = nullptr);
+	~HeaderEdit();
+
+	void setHeader(LDHeader* header);
+	LDHeader* header() const;
+	bool hasValidHeader() const;
+
+signals:
+	void descriptionChanged(const QString& newDescription);
+	void windingChanged(Winding newWinding);
+
+private:
+	class Ui_HeaderEdit& ui;
+	class HeaderHistoryModel* headerHistoryModel = nullptr;
+	LDHeader* m_header = nullptr;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/widgets/headeredit.ui	Fri Mar 16 16:28:39 2018 +0200
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HeaderEdit</class>
+ <widget class="QWidget" name="HeaderEdit">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>405</width>
+    <height>429</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>2</number>
+     </property>
+     <widget class="QWidget" name="tab">
+      <attribute name="title">
+       <string>File manifest</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <widget class="QLineEdit" name="description">
+         <property name="placeholderText">
+          <string>Description</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <layout class="QFormLayout" name="formLayout">
+         <item row="0" column="0">
+          <widget class="QLabel" name="label">
+           <property name="text">
+            <string>Author:</string>
+           </property>
+           <property name="buddy">
+            <cstring>author</cstring>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QLineEdit" name="author"/>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="label_2">
+           <property name="text">
+            <string>T&amp;ype:</string>
+           </property>
+           <property name="buddy">
+            <cstring>type</cstring>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QComboBox" name="type">
+           <item>
+            <property name="text">
+             <string>Part</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Subpart</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Shortcut</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Primitive</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>8 Primitive</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>48 Primitive</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Configuration</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="label_3">
+           <property name="text">
+            <string>&amp;License:</string>
+           </property>
+           <property name="buddy">
+            <cstring>license</cstring>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1">
+          <widget class="QComboBox" name="license">
+           <item>
+            <property name="text">
+             <string>(unspecified)</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>CCAL 2.0</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Non-CA</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="6" column="1">
+          <layout class="QGridLayout" name="gridLayout_2">
+           <item row="0" column="1">
+            <widget class="QCheckBox" name="physicalColor">
+             <property name="text">
+              <string>Physical colour shortcut</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QCheckBox" name="alias">
+             <property name="text">
+              <string>Alias</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QCheckBox" name="flexibleSection">
+             <property name="text">
+              <string>Flexible section</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+         <item row="3" column="0">
+          <widget class="QLabel" name="label_5">
+           <property name="text">
+            <string>Category:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="4" column="0">
+          <widget class="QLabel" name="label_6">
+           <property name="text">
+            <string>Cmdline:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="5" column="0">
+          <widget class="QLabel" name="label_7">
+           <property name="text">
+            <string>Winding:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="1">
+          <widget class="QComboBox" name="category"/>
+         </item>
+         <item row="5" column="1">
+          <widget class="QComboBox" name="winding">
+           <item>
+            <property name="text">
+             <string>✕ None</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>↶ Counter-clockwise</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>↷ Clockwise</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="4" column="1">
+          <widget class="QLineEdit" name="cmdline"/>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_2">
+      <attribute name="title">
+       <string>Help and keywords</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_5">
+       <item>
+        <widget class="QGroupBox" name="groupBox_2">
+         <property name="title">
+          <string>Help section</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_3">
+          <item>
+           <widget class="QPlainTextEdit" name="help"/>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="groupBox">
+         <property name="title">
+          <string>Keywords</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_4">
+          <item>
+           <widget class="QPlainTextEdit" name="keywords"/>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_3">
+      <attribute name="title">
+       <string>History</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_6">
+       <item>
+        <widget class="QTableView" name="history">
+         <attribute name="horizontalHeaderStretchLastSection">
+          <bool>true</bool>
+         </attribute>
+         <attribute name="verticalHeaderVisible">
+          <bool>false</bool>
+         </attribute>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

mercurial