# HG changeset patch # User Teemu Piippo # Date 1521210519 -7200 # Node ID 9c570a30c98a3146d29d5964e81b3f84f19ba56c # Parent 8db26042f3d14dc7f248b3aca8f49c9d434c0ad7 Added basic header editing diff -r 8db26042f3d1 -r 9c570a30c98a CMakeLists.txt --- 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") diff -r 8db26042f3d1 -r 9c570a30c98a src/basics.h --- 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 +void assignFlag(QFlags& flagset, bool value) +{ + if (value) + flagset |= static_cast(Flag); + else + flagset &= ~static_cast(Flag); +} diff -r 8db26042f3d1 -r 9c570a30c98a src/documentmanager.cpp --- 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(); diff -r 8db26042f3d1 -r 9c570a30c98a src/headerhistorymodel.cpp --- /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(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(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(); +} diff -r 8db26042f3d1 -r 9c570a30c98a src/headerhistorymodel.h --- /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 + +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; +}; diff -r 8db26042f3d1 -r 9c570a30c98a src/lddocument.cpp --- 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(static_cast(one) ^ static_cast(other)); } diff -r 8db26042f3d1 -r 9c570a30c98a src/lddocument.h --- 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 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 history; + Winding winding = NoWinding; enum { - NoWinding, - CounterClockwise, - Clockwise, - } winding = NoWinding; - enum - { + Unspecified, CaLicense, NonCaLicense - } license = CaLicense; + } license = Unspecified; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QFlags) - -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; diff -r 8db26042f3d1 -r 9c570a30c98a src/mainwindow.cpp --- 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 (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) diff -r 8db26042f3d1 -r 9c570a30c98a src/mainwindow.h --- 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); diff -r 8db26042f3d1 -r 9c570a30c98a src/mainwindow.ui --- 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 @@ 0 + + + Header + + + + + + + 0 0 465 - 399 + 362 @@ -76,7 +86,7 @@ 0 0 465 - 399 + 362 @@ -158,7 +168,7 @@ 0 0 465 - 399 + 362 @@ -1723,6 +1733,14 @@ + + + HeaderEdit + QWidget +
widgets/headeredit.h
+ 1 +
+
diff -r 8db26042f3d1 -r 9c570a30c98a src/parser.cpp --- 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") diff -r 8db26042f3d1 -r 9c570a30c98a src/widgets/headeredit.cpp --- /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(&QComboBox::currentIndexChanged), + [&](int index) + { + if (this->hasValidHeader()) + { + this->m_header->winding = static_cast(index); + emit windingChanged(this->m_header->winding); + } + } + ); + connect( + ui.license, + qOverload(&QComboBox::currentIndexChanged), + [&](int index) + { + if (this->m_header) + this->m_header->license = static_cast(index); + } + ); + connect( + ui.category, + qOverload(&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(this->m_header->qualfiers, state == Qt::Checked); + } + ); + connect( + ui.physicalColor, + &QCheckBox::stateChanged, + [&](int state) + { + if (this->hasValidHeader()) + { + assignFlag( + this->m_header->qualfiers, + state == Qt::Checked + ); + } + } + ); + connect( + ui.flexibleSection, + &QCheckBox::stateChanged, + [&](int state) + { + if (this->hasValidHeader()) + { + assignFlag( + 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(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; +} diff -r 8db26042f3d1 -r 9c570a30c98a src/widgets/headeredit.h --- /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 +#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; +}; diff -r 8db26042f3d1 -r 9c570a30c98a src/widgets/headeredit.ui --- /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 @@ + + + HeaderEdit + + + + 0 + 0 + 405 + 429 + + + + Form + + + + + + 2 + + + + File manifest + + + + + + Description + + + + + + + + + Author: + + + author + + + + + + + + + + T&ype: + + + type + + + + + + + + Part + + + + + Subpart + + + + + Shortcut + + + + + Primitive + + + + + 8 Primitive + + + + + 48 Primitive + + + + + Configuration + + + + + + + + &License: + + + license + + + + + + + + (unspecified) + + + + + CCAL 2.0 + + + + + Non-CA + + + + + + + + + + Physical colour shortcut + + + + + + + Alias + + + + + + + Flexible section + + + + + + + + + Category: + + + + + + + Cmdline: + + + + + + + Winding: + + + + + + + + + + + ✕ None + + + + + ↶ Counter-clockwise + + + + + ↷ Clockwise + + + + + + + + + + + + + + Help and keywords + + + + + + Help section + + + + + + + + + + + + Keywords + + + + + + + + + + + + + History + + + + + + true + + + false + + + + + + + + + + + +