# HG changeset patch # User Teemu Piippo # Date 1455577190 -7200 # Node ID 49358df9495b6570a2812348eadc60f19675a334 # Parent fc1c13db961847fe96cb65da87321b38d8a49340 Transformed primitive management into a new class PrimitiveManager that is a member of MainWindow diff -r fc1c13db9618 -r 49358df9495b src/addObjectDialog.cpp --- a/src/addObjectDialog.cpp Sun Feb 14 03:19:28 2016 +0200 +++ b/src/addObjectDialog.cpp Tue Feb 16 00:59:50 2016 +0200 @@ -95,8 +95,13 @@ coordCount = 3; tw_subfileList = new QTreeWidget(); tw_subfileList->setHeaderLabel (tr ("Primitives")); - populatePrimitivesTree (tw_subfileList, - (obj ? static_cast (obj)->fileInfo()->name() : "")); + + QString defaultname; + + if (obj) + defaultname = static_cast (obj)->fileInfo()->name(); + + g_win->primitives()->populateTreeWidget(tw_subfileList, defaultname); connect (tw_subfileList, SIGNAL (itemSelectionChanged()), this, SLOT (slot_subfileTypeChanged())); lb_subfileName = new QLabel ("File:"); le_subfileName = new QLineEdit; diff -r fc1c13db9618 -r 49358df9495b src/editmodes/circleMode.cpp --- a/src/editmodes/circleMode.cpp Sun Feb 14 03:19:28 2016 +0200 +++ b/src/editmodes/circleMode.cpp Tue Feb 16 00:59:50 2016 +0200 @@ -91,14 +91,14 @@ if (dist0 == dist1) { // If the radii are the same, there's no ring space to fill. Use a circle. - refFile = GetPrimitive (::Circle, segments, divisions, 0); + refFile = primitives()->getPrimitive(::Circle, segments, divisions, 0); transform = getCircleDrawMatrix (dist0); circleOrDisc = true; } else if (dist0 == 0 or dist1 == 0) { // If either radii is 0, use a disc. - refFile = GetPrimitive (::Disc, segments, divisions, 0); + refFile = primitives()->getPrimitive(::Disc, segments, divisions, 0); transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); circleOrDisc = true; } @@ -107,7 +107,7 @@ // The ring finder found a solution, use that. Add the component rings to the file. for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) { - refFile = GetPrimitive (::Ring, segments, divisions, cmp.num); + refFile = primitives()->getPrimitive(::Ring, segments, divisions, cmp.num); LDSubfileReference* ref = LDSpawn(); ref->setFileInfo (refFile); ref->setTransform (getCircleDrawMatrix (cmp.scale)); @@ -132,8 +132,8 @@ templ.setCoordinate (localz, renderer()->getDepthValue()); // Calculate circle coords - MakeCircle (segments, divisions, dist0, c0); - MakeCircle (segments, divisions, dist1, c1); + primitives()->makeCircle(segments, divisions, dist0, c0); + primitives()->makeCircle(segments, divisions, dist1, c1); for (int i = 0; i < segments; ++i) { diff -r fc1c13db9618 -r 49358df9495b src/hierarchyelement.cpp --- a/src/hierarchyelement.cpp Sun Feb 14 03:19:28 2016 +0200 +++ b/src/hierarchyelement.cpp Tue Feb 16 00:59:50 2016 +0200 @@ -22,6 +22,8 @@ #include "mainwindow.h" #include "guiutilities.h" + + HierarchyElement::HierarchyElement (QObject* parent) : m_window (nullptr) { @@ -36,7 +38,7 @@ if (m_window == nullptr) { m_window = g_win; - print ("WARNING: Hierarchy element instance %p should be in the hierarchy of a " + print ("WARNING: Hierarchy element instance %1 should be in the hierarchy of a " "MainWindow but isn't.\n", this); } @@ -44,17 +46,30 @@ m_config = m_window->config(); } + + GuiUtilities* HierarchyElement::guiUtilities() const { return m_window->guiUtilities(); } + + LDDocument* HierarchyElement::currentDocument() { return m_window->currentDocument(); } + + const LDObjectList& HierarchyElement::selectedObjects() { return m_window->selectedObjects(); } + + + +PrimitiveManager* HierarchyElement::primitives() +{ + return m_window->primitives(); +} diff -r fc1c13db9618 -r 49358df9495b src/hierarchyelement.h --- a/src/hierarchyelement.h Sun Feb 14 03:19:28 2016 +0200 +++ b/src/hierarchyelement.h Tue Feb 16 00:59:50 2016 +0200 @@ -25,6 +25,7 @@ class GuiUtilities; class LDDocument; class DocumentManager; +class PrimitiveManager; // // Objects that are to take part in the MainWindow's hierarchy multiple-inherit from this class to get a few useful @@ -38,6 +39,7 @@ const LDObjectList& selectedObjects(); LDDocument* currentDocument(); GuiUtilities* guiUtilities() const; + PrimitiveManager* primitives(); protected: MainWindow* m_window; diff -r fc1c13db9618 -r 49358df9495b src/main.cpp --- a/src/main.cpp Sun Feb 14 03:19:28 2016 +0200 +++ b/src/main.cpp Tue Feb 16 00:59:50 2016 +0200 @@ -22,17 +22,16 @@ #include #include #include -#include "mainwindow.h" #include "ldDocument.h" #include "miscallenous.h" #include "colors.h" #include "basics.h" -#include "primitives.h" #include "glRenderer.h" #include "dialogs.h" #include "crashCatcher.h" #include "ldpaths.h" #include "documentmanager.h" +#include "mainwindow.h" MainWindow* g_win = nullptr; Configuration* Config = nullptr; @@ -59,7 +58,6 @@ initCrashCatcher(); initColors(); MainWindow* win = new MainWindow(configObject); - LoadPrimitives(); win->show(); // Process the command line diff -r fc1c13db9618 -r 49358df9495b src/mainwindow.cpp --- a/src/mainwindow.cpp Sun Feb 14 03:19:28 2016 +0200 +++ b/src/mainwindow.cpp Tue Feb 16 00:59:50 2016 +0200 @@ -68,6 +68,7 @@ QMainWindow (parent, flags), m_config(config), m_guiUtilities (new GuiUtilities (this)), + m_primitives(new PrimitiveManager(this)), ui (*new Ui_MainWindow), m_externalPrograms (nullptr), m_settings (makeSettings (this)), @@ -96,8 +97,8 @@ connect (m_tabs, SIGNAL (currentChanged(int)), this, SLOT (tabSelected())); connect (m_tabs, SIGNAL (tabCloseRequested (int)), this, SLOT (closeTab (int))); - if (ActivePrimitiveScanner()) - connect (ActivePrimitiveScanner(), SIGNAL (workDone()), this, SLOT (updatePrimitives())); + if (m_primitives->activeScanner()) + connect (m_primitives->activeScanner(), SIGNAL (workDone()), this, SLOT (updatePrimitives())); else updatePrimitives(); @@ -177,6 +178,13 @@ dialog->show(); m_config.setFirstStart (false); } + + QMetaObject::invokeMethod (this, "finishInitialization", Qt::QueuedConnection); +} + +void MainWindow::finishInitialization() +{ + m_primitives->loadPrimitives(); } MainWindow::~MainWindow() @@ -908,6 +916,14 @@ // --------------------------------------------------------------------------------------------------------------------- // +void errorPrompt (QWidget* parent, const QString& message) +{ + QMessageBox::critical (parent, MainWindow::tr ("Error"), message, + (QMessageBox::Close), QMessageBox::Close); +} + +// --------------------------------------------------------------------------------------------------------------------- +// void MainWindow::updateDocumentList() { m_updatingTabs = true; @@ -1031,6 +1047,13 @@ // --------------------------------------------------------------------------------------------------------------------- // +PrimitiveManager* MainWindow::primitives() +{ + return m_primitives; +} + +// --------------------------------------------------------------------------------------------------------------------- +// GLRenderer* MainWindow::renderer() { return m_renderer; @@ -1048,7 +1071,7 @@ // void MainWindow::updatePrimitives() { - populatePrimitivesTree (ui.primitives); + m_primitives->populateTreeWidget(ui.primitives); } // --------------------------------------------------------------------------------------------------------------------- @@ -1340,45 +1363,4 @@ void ColorToolbarItem::setToolButton (QToolButton* value) { m_toolButton = value; -} - -// --------------------------------------------------------------------------------------------------------------------- -// -void populatePrimitivesTree (QTreeWidget* tw, QString const& selectByDefault) -{ - tw->clear(); - - for (PrimitiveCategory* cat : g_PrimitiveCategories) - { - PrimitiveTreeItem* parentItem = new PrimitiveTreeItem (tw, nullptr); - parentItem->setText (0, cat->name()); - QList subfileItems; - - for (Primitive& prim : cat->prims) - { - PrimitiveTreeItem* item = new PrimitiveTreeItem (parentItem, &prim); - item->setText (0, format ("%1 - %2", prim.name, prim.title)); - subfileItems << item; - - // If this primitive is the one the current object points to, - // select it by default - if (selectByDefault == prim.name) - tw->setCurrentItem (item); - } - - tw->addTopLevelItem (parentItem); - } -} - -PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info) : - QTreeWidgetItem (parent), - m_primitive (info) {} - -PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidget* parent, Primitive* info) : - QTreeWidgetItem (parent), - m_primitive (info) {} - -Primitive* PrimitiveTreeItem::primitive() const -{ - return m_primitive; -} +} \ No newline at end of file diff -r fc1c13db9618 -r 49358df9495b src/mainwindow.h --- a/src/mainwindow.h Sun Feb 14 03:19:28 2016 +0200 +++ b/src/mainwindow.h Tue Feb 16 00:59:50 2016 +0200 @@ -35,9 +35,9 @@ class GLRenderer; class QComboBox; class QProgressBar; -struct Primitive; class Toolset; class Configuration; +class PrimitiveManager; class ColorToolbarItem { @@ -97,6 +97,7 @@ class GuiUtilities* guiUtilities(); void loadShortcuts(); LDDocument* newDocument (bool cache = false); + PrimitiveManager* primitives(); GLRenderer* renderer(); void refresh(); void refreshObjectList(); @@ -138,6 +139,7 @@ Configuration& m_config; class GuiUtilities* m_guiUtilities; GLRenderer* m_renderer; + PrimitiveManager* m_primitives; LDObjectList m_sel; QList m_quickColors; QList m_colorButtons; @@ -156,6 +158,7 @@ QMap m_defaultShortcuts; private slots: + void finishInitialization(); void selectionChanged(); void recentFileClicked(); void quickColorClicked(); @@ -180,6 +183,7 @@ // Displays an error prompt with the given message void Critical (const QString& message); +void errorPrompt (QWidget *parent, const QString& message); // Takes in pairs of radio buttons and respective values and finds the first selected one. // Returns returns the value of the first found radio button that was checked by the user. @@ -210,16 +214,4 @@ } } -class PrimitiveTreeItem : public QTreeWidgetItem -{ -public: - PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info); - PrimitiveTreeItem (QTreeWidget* parent, Primitive* info); - Primitive* primitive() const; - -private: - Primitive* m_primitive; -}; - -void populatePrimitivesTree (QTreeWidget* tw, const QString& selectByDefault = QString()); QSettings* makeSettings (QObject* parent = nullptr); diff -r fc1c13db9618 -r 49358df9495b src/primitives.cpp --- a/src/primitives.cpp Sun Feb 14 03:19:28 2016 +0200 +++ b/src/primitives.cpp Tue Feb 16 00:59:50 2016 +0200 @@ -28,10 +28,7 @@ #include "ldpaths.h" #include "documentmanager.h" -QList g_PrimitiveCategories; -QList g_primitives; -static PrimitiveScanner* g_activeScanner = nullptr; -PrimitiveCategory* g_unmatched = nullptr; +QList m_categories; static const QStringList g_radialNameRoots = { @@ -43,19 +40,19 @@ "con" }; -PrimitiveScanner* ActivePrimitiveScanner() +PrimitiveScanner* PrimitiveManager::activeScanner() { - return g_activeScanner; + return m_activeScanner; } -QString getPrimitivesCfgPath() +QString PrimitiveManager::getPrimitivesCfgPath() const { return qApp->applicationDirPath() + DIRSLASH "prims.cfg"; } // ============================================================================= // -void LoadPrimitives() +void PrimitiveManager::loadPrimitives() { // Try to load prims.cfg QFile conf (getPrimitivesCfgPath()); @@ -63,7 +60,7 @@ if (not conf.open (QIODevice::ReadOnly)) { // No prims.cfg, build it - PrimitiveScanner::start(); + startScan(); } else { @@ -85,16 +82,19 @@ Primitive info; info.name = line.left (space); info.title = line.mid (space + 1); - g_primitives << info; + m_primitives << info; } - PrimitiveCategory::populateCategories(); - print ("%1 primitives loaded.\n", g_primitives.size()); + populateCategories(); + print ("%1 primitives loaded.\n", m_primitives.size()); } } // ============================================================================= // +// TODO: replace with QDirIterator +// +static void GetRecursiveFilenames (QDir dir, QList& fnames) __attribute__((deprecated)); static void GetRecursiveFilenames (QDir dir, QList& fnames) { QFileInfoList flist = dir.entryInfoList (QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); @@ -110,51 +110,50 @@ // ============================================================================= // -PrimitiveScanner::PrimitiveScanner (QObject* parent) : - QObject (parent), - m_i (0) +PrimitiveScanner::PrimitiveScanner (PrimitiveManager* parent) : + QObject(parent), + HierarchyElement(parent), + m_manager(parent), + m_i(0) { - g_activeScanner = this; QDir dir = LDPaths::primitivesDir(); m_baselen = dir.absolutePath().length(); - GetRecursiveFilenames (dir, m_files); - emit starting (m_files.size()); - print ("Scanning primitives..."); + GetRecursiveFilenames(dir, m_files); + emit starting(m_files.size()); + print("Scanning primitives..."); } -// ============================================================================= -// -PrimitiveScanner::~PrimitiveScanner() +const QList& PrimitiveScanner::scannedPrimitives() const { - g_activeScanner = nullptr; + return m_prims; } // ============================================================================= // void PrimitiveScanner::work() { - int j = qMin (m_i + 100, m_files.size()); + int max = qMin (m_i + 100, m_files.size()); - for (; m_i < j; ++m_i) + for (; m_i < max; ++m_i) { - QString fname = m_files[m_i]; - QFile f (fname); + QString filename = m_files[m_i]; + QFile file (filename); - if (not f.open (QIODevice::ReadOnly)) + if (not file.open (QIODevice::ReadOnly)) continue; Primitive info; - info.name = fname.mid (m_baselen + 1); // make full path relative + info.name = filename.mid (m_baselen + 1); // make full path relative info.name.replace ('/', '\\'); // use DOS backslashes, they're expected info.category = nullptr; - QByteArray titledata = f.readLine(); + QByteArray titledata = file.readLine(); if (titledata != QByteArray()) info.title = QString::fromUtf8 (titledata); info.title = info.title.simplified(); - if (Q_LIKELY (info.title[0] == '0')) + if (info.title[0] == '0') { info.title.remove (0, 1); // remove 0 info.title = info.title.simplified(); @@ -166,26 +165,22 @@ if (m_i == m_files.size()) { // Done with primitives, now save to a config file - QString path = getPrimitivesCfgPath(); - QFile conf (path); + QString path = m_manager->getPrimitivesCfgPath(); + QFile configFile (path); - if (not conf.open (QIODevice::WriteOnly | QIODevice::Text)) - Critical (format ("Couldn't write primitive list %1: %2", - path, conf.errorString())); - else + if (configFile.open (QIODevice::WriteOnly | QIODevice::Text)) { for (Primitive& info : m_prims) - fprint (conf, "%1 %2\r\n", info.name, info.title); + fprint (configFile, "%1 %2\r\n", info.name, info.title); - conf.close(); + configFile.close(); + } + else + { + errorPrompt(m_window, format("Couldn't write primitive list %1: %2", path, configFile.errorString())); } - g_primitives = m_prims; - PrimitiveCategory::populateCategories(); - print ("%1 primitives scanned", g_primitives.size()); - g_activeScanner = nullptr; emit workDone(); - deleteLater(); } else { @@ -197,14 +192,27 @@ // ============================================================================= // -void PrimitiveScanner::start() +void PrimitiveManager::startScan() { - if (g_activeScanner) - return; + if (m_activeScanner == nullptr) + { + loadCategories(); + m_activeScanner = new PrimitiveScanner(this); + m_activeScanner->work(); + connect(m_activeScanner, &PrimitiveScanner::workDone, this, &PrimitiveManager::scanDone); + } +} - PrimitiveCategory::loadCategories(); - PrimitiveScanner* scanner = new PrimitiveScanner; - scanner->work(); +void PrimitiveManager::scanDone() +{ + if (m_activeScanner) + { + m_primitives = m_activeScanner->scannedPrimitives(); + populateCategories(); + print ("%1 primitives scanned", m_primitives.size()); + delete m_activeScanner; + m_activeScanner = nullptr; + } } // ============================================================================= @@ -215,62 +223,70 @@ // ============================================================================= // -void PrimitiveCategory::populateCategories() +void PrimitiveManager::clearCategories() +{ + for (PrimitiveCategory* category : m_categories) + delete category; + + m_categories.clear(); +} + +// ============================================================================= +// +void PrimitiveManager::populateCategories() { loadCategories(); - for (PrimitiveCategory* cat : g_PrimitiveCategories) - cat->prims.clear(); + for (PrimitiveCategory* category : m_categories) + category->primitives.clear(); - for (Primitive& prim : g_primitives) + for (Primitive& primitive : m_primitives) { bool matched = false; - prim.category = nullptr; + primitive.category = nullptr; // Go over the categories and their regexes, if and when there's a match, // the primitive's category is set to the category the regex beloings to. - for (PrimitiveCategory* cat : g_PrimitiveCategories) + for (PrimitiveCategory* category : m_categories) { - for (RegexEntry& entry : cat->regexes) + for (PrimitiveCategory::RegexEntry& entry : category->patterns) { switch (entry.type) { - case EFilenameRegex: - { - // f-regex, check against filename - matched = entry.regex.exactMatch (prim.name); - } break; + case PrimitiveCategory::FilenamePattern: + // f-regex, check against filename + matched = entry.regex.exactMatch (primitive.name); + break; - case ETitleRegex: - { - // t-regex, check against title - matched = entry.regex.exactMatch (prim.title); - } break; + case PrimitiveCategory::TitlePattern: + // t-regex, check against title + matched = entry.regex.exactMatch (primitive.title); + break; } if (matched) { - prim.category = cat; + primitive.category = category; break; } } - // Drop out if a category was decided on. - if (prim.category) + // Drop off if a category was decided on. + if (primitive.category) break; } // If there was a match, add the primitive to the category. // Otherwise, add it to the list of unmatched primitives. - if (prim.category) - prim.category->prims << prim; + if (primitive.category) + primitive.category->primitives << primitive; else - g_unmatched->prims << prim; + m_unmatched->primitives << primitive; } // Sort the categories. Note that we do this here because we need the existing // order for regex matching. - qSort (g_PrimitiveCategories.begin(), g_PrimitiveCategories.end(), + qSort (m_categories.begin(), m_categories.end(), [](PrimitiveCategory* const& a, PrimitiveCategory* const& b) -> bool { return a->name() < b->name(); @@ -279,12 +295,9 @@ // ============================================================================= // -void PrimitiveCategory::loadCategories() +void PrimitiveManager::loadCategories() { - for (PrimitiveCategory* cat : g_PrimitiveCategories) - delete cat; - - g_PrimitiveCategories.clear(); + clearCategories(); QString path = ":/data/primitive-categories.cfg"; QFile f (path); @@ -294,57 +307,67 @@ return; } - PrimitiveCategory* cat = nullptr; + PrimitiveCategory* category = nullptr; while (not f.atEnd()) { - QString line = f.readLine(); - int colon; - - if (line.endsWith ("\n")) - line.chop (1); + QString line = QString::fromUtf8(f.readLine()).trimmed(); if (line.length() == 0 or line[0] == '#') continue; - if ((colon = line.indexOf (":")) == -1) + int colon = line.indexOf (":"); + if (colon == -1) { - if (cat and cat->isValidToInclude()) - g_PrimitiveCategories << cat; + if (category and category->isValidToInclude()) + { + m_categories << category; + } + else if (category) + { + print (tr ("Warning: Category \"%1\" left without patterns"), category->name()); + delete category; + } - cat = new PrimitiveCategory (line); + category = new PrimitiveCategory (line); } - else if (cat) + else if (category) { - QString cmd = line.left (colon); - RegexType type = EFilenameRegex; + QString typechar = line.left (colon); + PrimitiveCategory::PatternType type = PrimitiveCategory::FilenamePattern; - if (cmd == "f") - type = EFilenameRegex; - else if (cmd == "t") - type = ETitleRegex; + if (typechar == "f") + { + type = PrimitiveCategory::FilenamePattern; + } + else if (typechar == "t") + { + type = PrimitiveCategory::TitlePattern; + } else { - print (tr ("Warning: unknown command \"%1\" on line \"%2\""), cmd, line); + print (tr ("Warning: unknown pattern type \"%1\" on line \"%2\""), typechar, line); continue; } QRegExp regex (line.mid (colon + 1)); - RegexEntry entry = { regex, type }; - cat->regexes << entry; + PrimitiveCategory::RegexEntry entry = { regex, type }; + category->patterns << entry; } else + { print ("Warning: Rules given before the first category name"); + } } - if (cat->isValidToInclude()) - g_PrimitiveCategories << cat; + if (category->isValidToInclude()) + m_categories << category; // Add a category for unmatched primitives. // Note: if this function is called the second time, g_unmatched has been // deleted at the beginning of the function and is dangling at this point. - g_unmatched = new PrimitiveCategory (tr ("Other")); - g_PrimitiveCategories << g_unmatched; + m_unmatched = new PrimitiveCategory (tr ("Other")); + m_categories << m_unmatched; f.close(); } @@ -352,14 +375,7 @@ // bool PrimitiveCategory::isValidToInclude() { - if (regexes.isEmpty()) - { - print (tr ("Warning: category \"%1\" left without patterns"), name()); - deleteLater(); - return false; - } - - return true; + return not patterns.isEmpty(); } QString PrimitiveCategory::name() const @@ -369,28 +385,23 @@ // ============================================================================= // -bool IsPrimitiveLoaderBusy() -{ - return g_activeScanner; -} - -// ============================================================================= -// -static double GetRadialPoint (int i, int divs, double (*func) (double)) +static double getRadialPoint (int i, int divs, double (*func) (double)) { return (*func) ((i * 2 * Pi) / divs); } // ============================================================================= // -void MakeCircle (int segs, int divs, double radius, QList& lines) +// TODO: this doesn't really belong here. +// +void PrimitiveManager::makeCircle (int segs, int divs, double radius, QList& lines) { for (int i = 0; i < segs; ++i) { - double x0 = radius * GetRadialPoint (i, divs, cos), - x1 = radius * GetRadialPoint (i + 1, divs, cos), - z0 = radius * GetRadialPoint (i, divs, sin), - z1 = radius * GetRadialPoint (i + 1, divs, sin); + double x0 = radius * getRadialPoint (i, divs, cos), + x1 = radius * getRadialPoint (i + 1, divs, cos), + z0 = radius * getRadialPoint (i, divs, sin), + z1 = radius * getRadialPoint (i + 1, divs, sin); lines << QLineF (QPointF (x0, z0), QPointF (x1, z1)); } @@ -398,24 +409,24 @@ // ============================================================================= // -LDObjectList MakePrimitive (PrimitiveType type, int segs, int divs, int num) +LDObjectList PrimitiveManager::makePrimitiveBody (PrimitiveType type, int segs, int divs, int num) { LDObjectList objs; - QList condLineSegs; + QList conditionalLineSegments; QList circle; - MakeCircle (segs, divs, 1, circle); + makeCircle (segs, divs, 1, circle); for (int i = 0; i < segs; ++i) { - double x0 = circle[i].x1(), - x1 = circle[i].x2(), - z0 = circle[i].y1(), - z1 = circle[i].y2(); + double x0 = circle[i].x1(); + double x1 = circle[i].x2(); + double z0 = circle[i].y1(); + double z1 = circle[i].y2(); switch (type) { - case Circle: + case Circle: { Vertex v0 (x0, 0.0f, z0), v1 (x1, 0.0f, z1); @@ -425,11 +436,12 @@ line->setVertex (1, v1); line->setColor (EdgeColor); objs << line; - } break; + } + break; - case Cylinder: - case Ring: - case Cone: + case Cylinder: + case Ring: + case Cone: { double x2, x3, z2, z3; double y0, y1, y2, y3; @@ -465,10 +477,10 @@ } } - Vertex v0 (x0, y0, z0), - v1 (x1, y1, z1), - v2 (x2, y2, z2), - v3 (x3, y3, z3); + Vertex v0 (x0, y0, z0); + Vertex v1 (x1, y1, z1); + Vertex v2 (x2, y2, z2); + Vertex v3 (x3, y3, z3); LDQuad* quad (LDSpawn (v0, v1, v2, v3)); quad->setColor (MainColor); @@ -479,11 +491,12 @@ objs << quad; if (type == Cylinder or type == Cone) - condLineSegs << i; - } break; + conditionalLineSegments << i; + } + break; - case Disc: - case DiscNeg: + case Disc: + case DiscNegative: { double x2, z2; @@ -507,21 +520,22 @@ seg->setVertex (1, v1); seg->setVertex (type == Disc ? 2 : 0, v2); objs << seg; - } break; + } + break; } } // If this is not a full circle, we need a conditional line at the other // end, too. - if (segs < divs and condLineSegs.size() != 0) - condLineSegs << segs; + if (segs < divs and not conditionalLineSegments.isEmpty()) + conditionalLineSegments << segs; - for (int i : condLineSegs) + for (int i : conditionalLineSegments) { - Vertex v0 (GetRadialPoint (i, divs, cos), 0.0f, GetRadialPoint (i, divs, sin)), - v1, - v2 (GetRadialPoint (i + 1, divs, cos), 0.0f, GetRadialPoint (i + 1, divs, sin)), - v3 (GetRadialPoint (i - 1, divs, cos), 0.0f, GetRadialPoint (i - 1, divs, sin)); + Vertex v0 (getRadialPoint (i, divs, cos), 0.0f, getRadialPoint (i, divs, sin)); + Vertex v1; + Vertex v2 (getRadialPoint (i + 1, divs, cos), 0.0f, getRadialPoint (i + 1, divs, sin)); + Vertex v3 (getRadialPoint (i - 1, divs, cos), 0.0f, getRadialPoint (i - 1, divs, sin)); if (type == Cylinder) { @@ -549,36 +563,37 @@ // ============================================================================= // -static QString PrimitiveTypeName (PrimitiveType type) +QString PrimitiveManager::primitiveTypeName (PrimitiveType type) { // Not translated as primitives are in English. - return type == Circle ? "Circle" : - type == Cylinder ? "Cylinder" : - type == Disc ? "Disc" : - type == DiscNeg ? "Disc Negative" : - type == Ring ? "Ring" : "Cone"; + const char* names[] = { "Circle", "Cylinder", "Disc", "Disc Negative", "Ring", "Cone" }; + + if (type >= 0 and type < countof(names)) + return names[type]; + else + return "Unknown"; } // ============================================================================= // -QString MakeRadialFileName (PrimitiveType type, int segs, int divs, int num) +QString PrimitiveManager::makeRadialFileName (PrimitiveType type, int segs, int divs, int num) { - int numer = segs, - denom = divs; + int numerator = segs; + int denominator = divs; // Simplify the fractional part, but the denominator must be at least 4. - simplify (numer, denom); + simplify (numerator, denominator); - if (denom < 4) + if (denominator < 4) { - const int factor = 4 / denom; - numer *= factor; - denom *= factor; + const int factor = 4 / denominator; + numerator *= factor; + denominator *= factor; } // Compose some general information: prefix, fraction, root, ring number QString prefix = (divs == LowResolution) ? "" : format ("%1/", divs); - QString frac = format ("%1-%2", numer, denom); + QString frac = format ("%1-%2", numerator, denominator); QString root = g_radialNameRoots[type]; QString numstr = (type == Ring or type == Cone) ? format ("%1", num) : ""; @@ -593,51 +608,50 @@ // ============================================================================= // -LDDocument* GeneratePrimitive (PrimitiveType type, int segs, int divs, int num) +LDDocument* PrimitiveManager::generatePrimitive (PrimitiveType type, int segments, int divisions, int number) { // Make the description - QString frac = QString::number ((float) segs / divs); - QString name = MakeRadialFileName (type, segs, divs, num); - QString descr; + QString fraction = QString::number ((float) segments / divisions); + QString name = makeRadialFileName (type, segments, divisions, number); + QString description; // Ensure that there's decimals, even if they're 0. - if (frac.indexOf (".") == -1) - frac += ".0"; + if (fraction.indexOf (".") == -1) + fraction += ".0"; if (type == Ring or type == Cone) { QString spacing = - (num < 10) ? " " : - (num < 100) ? " " : ""; + (number < 10) ? " " : + (number < 100) ? " " : ""; - descr = format ("%1 %2%3 x %4", PrimitiveTypeName (type), spacing, num, frac); + description = format ("%1 %2%3 x %4", primitiveTypeName (type), spacing, number, fraction); } else - descr = format ("%1 %2", PrimitiveTypeName (type), frac); + description = format ("%1 %2", primitiveTypeName (type), fraction); // Prepend "Hi-Res" if 48/ primitive. - if (divs == HighResolution) - descr.insert (0, "Hi-Res "); + if (divisions == HighResolution) + description.insert (0, "Hi-Res "); - LDDocument* document = g_win->newDocument(); + LDDocument* document = m_window->newDocument(); document->setDefaultName (name); QString author = APPNAME; QString license = ""; - if (not Config->defaultName().isEmpty()) + if (not m_config->defaultName().isEmpty()) { license = PreferredLicenseText(); - author = format ("%1 [%2]", Config->defaultName(), Config->defaultUser()); + author = format ("%1 [%2]", m_config->defaultName(), m_config->defaultUser()); } LDObjectList objs; - objs << LDSpawn (descr) + objs << LDSpawn (description) << LDSpawn (format ("Name: %1", name)) << LDSpawn (format ("Author: %1", author)) - << LDSpawn (format ("!LDRAW_ORG Unofficial_%1Primitive", - divs == HighResolution ? "48_" : "")) + << LDSpawn (format ("!LDRAW_ORG Unofficial_%1Primitive", divisions == HighResolution ? "48_" : "")) << LDSpawn (license) << LDSpawn() << LDSpawn (BfcStatement::CertifyCCW) @@ -646,22 +660,25 @@ document->openForEditing(); document->history()->setIgnoring (false); document->addObjects (objs); - document->addObjects (MakePrimitive (type, segs, divs, num)); + document->addObjects (makePrimitiveBody (type, segments, divisions, number)); document->addHistoryStep(); return document; } // ============================================================================= // -LDDocument* GetPrimitive (PrimitiveType type, int segs, int divs, int num) +// Gets a primitive by the given specs. If the primitive cannot be found, it will +// be automatically generated. +// +LDDocument* PrimitiveManager::getPrimitive (PrimitiveType type, int segs, int divs, int num) { - QString name = MakeRadialFileName (type, segs, divs, num); - LDDocument* f = g_win->documents()->getDocumentByName (name); + QString name = makeRadialFileName (type, segs, divs, num); + LDDocument* document = m_window->documents()->getDocumentByName (name); - if (f) - return f; + if (document) + return document; - return GeneratePrimitive (type, segs, divs, num); + return generatePrimitive (type, segs, divs, num); } // ============================================================================= @@ -692,3 +709,58 @@ if (on and ui->sb_segs->value() == LowResolution) ui->sb_segs->setValue (HighResolution); } + +// ============================================================================= +// +PrimitiveManager::PrimitiveManager(QObject* parent) : + QObject(parent), + HierarchyElement(parent), + m_activeScanner(nullptr), + m_unmatched(nullptr) {} + +// --------------------------------------------------------------------------------------------------------------------- +// +void PrimitiveManager::populateTreeWidget(QTreeWidget* tree, const QString& selectByDefault) +{ + tree->clear(); + + for (PrimitiveCategory* category : m_categories) + { + PrimitiveTreeItem* parentItem = new PrimitiveTreeItem (tree, nullptr); + parentItem->setText (0, category->name()); + QList subfileItems; + + for (Primitive& prim : category->primitives) + { + PrimitiveTreeItem* item = new PrimitiveTreeItem (parentItem, &prim); + item->setText (0, format ("%1 - %2", prim.name, prim.title)); + subfileItems << item; + + // If this primitive is the one the current object points to, + // select it by default + if (selectByDefault == prim.name) + tree->setCurrentItem (item); + } + + tree->addTopLevelItem (parentItem); + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// +PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info) : + QTreeWidgetItem (parent), + m_primitive (info) {} + +// --------------------------------------------------------------------------------------------------------------------- +// +PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidget* parent, Primitive* info) : + QTreeWidgetItem (parent), + m_primitive (info) {} + +// --------------------------------------------------------------------------------------------------------------------- +// +Primitive* PrimitiveTreeItem::primitive() const +{ + return m_primitive; +} diff -r fc1c13db9618 -r 49358df9495b src/primitives.h --- a/src/primitives.h Sun Feb 14 03:19:28 2016 +0200 +++ b/src/primitives.h Tue Feb 16 00:59:50 2016 +0200 @@ -17,10 +17,10 @@ */ #pragma once -#include "main.h" -#include "basics.h" #include #include +#include +#include "main.h" class LDDocument; class Ui_MakePrimUI; @@ -28,9 +28,9 @@ struct Primitive { - QString name; - QString title; - PrimitiveCategory* category; + QString name; + QString title; + PrimitiveCategory* category; }; class PrimitiveCategory : public QObject @@ -38,28 +38,25 @@ Q_OBJECT public: - enum RegexType + enum PatternType { - EFilenameRegex, - ETitleRegex + FilenamePattern, + TitlePattern }; struct RegexEntry { QRegExp regex; - RegexType type; + PatternType type; }; - QList regexes; - QList prims; + QList patterns; + QList primitives; explicit PrimitiveCategory (QString name, QObject* parent = 0); bool isValidToInclude(); QString name() const; - static void loadCategories(); - static void populateCategories(); - private: QString m_name; }; @@ -68,41 +65,38 @@ // Worker object that scans the primitives folder for primitives and // builds an index of them. // -class PrimitiveScanner : public QObject +class PrimitiveScanner : public QObject, HierarchyElement { Q_OBJECT public: - explicit PrimitiveScanner (QObject* parent = 0); - virtual ~PrimitiveScanner(); - static void start(); + PrimitiveScanner(PrimitiveManager* parent); + const QList& scannedPrimitives() const; public slots: - void work(); + void work(); signals: - void starting (int num); - void workDone(); - void update (int i); + void starting(int num); + void workDone(); + void update(int i); private: - QList m_prims; - QStringList m_files; - int m_i; - int m_baselen; + PrimitiveManager* m_manager; + QList m_prims; + QStringList m_files; + int m_i; + int m_baselen; }; -extern QList g_PrimitiveCategories; - -void LoadPrimitives(); -PrimitiveScanner* ActivePrimitiveScanner(); +extern QList m_categories; enum PrimitiveType { Circle, Cylinder, Disc, - DiscNeg, + DiscNegative, Ring, Cone, }; @@ -120,11 +114,44 @@ void hiResToggled (bool on); }; -void MakeCircle (int segs, int divs, double radius, QList& lines); -LDDocument* GeneratePrimitive (PrimitiveType type, int segs, int divs, int num); +class PrimitiveManager : public QObject, HierarchyElement +{ + Q_OBJECT + +public: + PrimitiveManager(QObject* parent); + + PrimitiveScanner* activeScanner(); + LDDocument* generatePrimitive(PrimitiveType type, int segs, int divs, int num); + LDDocument* getPrimitive(PrimitiveType type, int segs, int divs, int num); + QString getPrimitivesCfgPath() const; + void loadPrimitives(); + void makeCircle(int segs, int divs, double radius, QList& lines); + QString makeRadialFileName(PrimitiveType type, int segs, int divs, int num); + void populateTreeWidget(QTreeWidget* tree, const QString& selectByDefault = QString()); + QString primitiveTypeName(PrimitiveType type); + Q_SLOT void scanDone(); + void startScan(); -// Gets a primitive by the given specs. If the primitive cannot be found, it will -// be automatically generated. -LDDocument* GetPrimitive (PrimitiveType type, int segs, int divs, int num); +private: + QList m_categories; + PrimitiveScanner* m_activeScanner; + QList m_primitives; + PrimitiveCategory* m_unmatched; -QString MakeRadialFileName (PrimitiveType type, int segs, int divs, int num); + LDObjectList makePrimitiveBody (PrimitiveType type, int segs, int divs, int num); + void loadCategories(); + void populateCategories(); + void clearCategories(); +}; + +class PrimitiveTreeItem : public QTreeWidgetItem +{ +public: + PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info); + PrimitiveTreeItem (QTreeWidget* parent, Primitive* info); + Primitive* primitive() const; + +private: + Primitive* m_primitive; +}; diff -r fc1c13db9618 -r 49358df9495b src/toolsets/filetoolset.cpp --- a/src/toolsets/filetoolset.cpp Sun Feb 14 03:19:28 2016 +0200 +++ b/src/toolsets/filetoolset.cpp Tue Feb 16 00:59:50 2016 +0200 @@ -172,7 +172,7 @@ void FileToolset::scanPrimitives() { - PrimitiveScanner::start(); + primitives()->startScan(); } void FileToolset::openSubfiles() @@ -200,24 +200,25 @@ void FileToolset::makePrimitive() { - PrimitivePrompt* dlg = new PrimitivePrompt (m_window); + PrimitivePrompt* dialog = new PrimitivePrompt (m_window); - if (not dlg->exec()) + if (not dialog->exec()) return; - int segs = dlg->ui->sb_segs->value(); - int divs = dlg->ui->cb_hires->isChecked() ? HighResolution : LowResolution; - int num = dlg->ui->sb_ringnum->value(); + int segs = dialog->ui->sb_segs->value(); + int divs = dialog->ui->cb_hires->isChecked() ? HighResolution : LowResolution; + int num = dialog->ui->sb_ringnum->value(); + PrimitiveType type = - dlg->ui->rb_circle->isChecked() ? Circle : - dlg->ui->rb_cylinder->isChecked() ? Cylinder : - dlg->ui->rb_disc->isChecked() ? Disc : - dlg->ui->rb_ndisc->isChecked() ? DiscNeg : - dlg->ui->rb_ring->isChecked() ? Ring : Cone; + dialog->ui->rb_circle->isChecked() ? Circle : + dialog->ui->rb_cylinder->isChecked() ? Cylinder : + dialog->ui->rb_disc->isChecked() ? Disc : + dialog->ui->rb_ndisc->isChecked() ? DiscNegative : + dialog->ui->rb_ring->isChecked() ? Ring : Cone; - LDDocument* f = GeneratePrimitive (type, segs, divs, num); - f->openForEditing(); - m_window->save (f, false); + LDDocument* primitive = primitives()->generatePrimitive(type, segs, divs, num); + primitive->openForEditing(); + m_window->save(primitive, false); } // These are not exactly file tools but I don't want to make another toolset just for 3 very small actions