diff -r 3d8ab0f89102 -r 450827da2376 src/actions/EditActions.cc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/actions/EditActions.cc Tue Jan 21 02:09:14 2014 +0200
@@ -0,0 +1,837 @@
+/*
+ * LDForge: LDraw parts authoring CAD
+ * Copyright (C) 2013, 2014 Santeri Piippo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include "../MainWindow.h"
+#include "../Main.h"
+#include "../Document.h"
+#include "../ColorSelector.h"
+#include "../Misc.h"
+#include "../Widgets.h"
+#include "../GLRenderer.h"
+#include "../Dialogs.h"
+#include "../Colors.h"
+#include "ui_replcoords.h"
+#include "ui_editraw.h"
+#include "ui_flip.h"
+#include "ui_addhistoryline.h"
+
+cfg (Bool, edit_schemanticinline, false);
+extern_cfg (String, ld_defaultuser);
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+static int copyToClipboard()
+{
+ LDObjectList objs = selection();
+ int num = 0;
+
+ // Clear the clipboard first.
+ qApp->clipboard()->clear();
+
+ // Now, copy the contents into the clipboard.
+ QString data;
+
+ for (LDObject* obj : objs)
+ {
+ if (data.length() > 0)
+ data += "\n";
+
+ data += obj->raw();
+ ++num;
+ }
+
+ qApp->clipboard()->setText (data);
+ return num;
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Cut, CTRL (X))
+{
+ int num = copyToClipboard();
+ deleteSelection();
+ log (tr ("%1 objects cut"), num);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Copy, CTRL (C))
+{
+ int num = copyToClipboard();
+ log (tr ("%1 objects copied"), num);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Paste, CTRL (V))
+{
+ const QString clipboardText = qApp->clipboard()->text();
+ int idx = getInsertionPoint();
+ getCurrentDocument()->clearSelection();
+ int num = 0;
+
+ for (QString line : clipboardText.split ("\n"))
+ {
+ LDObject* pasted = parseLine (line);
+ getCurrentDocument()->insertObj (idx++, pasted);
+ pasted->select();
+ R()->compileObject (pasted);
+ ++num;
+ }
+
+ log (tr ("%1 objects pasted"), num);
+ refresh();
+ scrollToSelection();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Delete, KEY (Delete))
+{
+ int num = deleteSelection();
+ log (tr ("%1 objects deleted"), num);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+static void doInline (bool deep)
+{
+ LDObjectList sel = selection();
+
+ for (LDObject* obj : sel)
+ {
+ // Get the index of the subfile so we know where to insert the
+ // inlined contents.
+ long idx = obj->getIndex();
+
+ if (idx == -1)
+ continue;
+
+ LDObjectList objs;
+
+ if (obj->getType() == LDObject::ESubfile)
+ objs = static_cast (obj)->inlineContents (
+ (LDSubfile::InlineFlags)
+ ( (deep) ? LDSubfile::DeepInline : 0) |
+ LDSubfile::CacheInline
+ );
+ else
+ continue;
+
+ // Merge in the inlined objects
+ for (LDObject * inlineobj : objs)
+ {
+ QString line = inlineobj->raw();
+ inlineobj->deleteSelf();
+ LDObject* newobj = parseLine (line);
+ getCurrentDocument()->insertObj (idx++, newobj);
+ newobj->select();
+ g_win->R()->compileObject (newobj);
+ }
+
+ // Delete the subfile now as it's been inlined.
+ obj->deleteSelf();
+ }
+
+ g_win->refresh();
+}
+
+DEFINE_ACTION (Inline, CTRL (I))
+{
+ doInline (false);
+}
+
+DEFINE_ACTION (InlineDeep, CTRL_SHIFT (I))
+{
+ doInline (true);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (SplitQuads, 0)
+{
+ LDObjectList objs = selection();
+ int num = 0;
+
+ for (LDObject* obj : objs)
+ {
+ if (obj->getType() != LDObject::EQuad)
+ continue;
+
+ // Find the index of this quad
+ long index = obj->getIndex();
+
+ if (index == -1)
+ return;
+
+ QList triangles = static_cast (obj)->splitToTriangles();
+
+ // Replace the quad with the first triangle and add the second triangle
+ // after the first one.
+ getCurrentDocument()->setObject (index, triangles[0]);
+ getCurrentDocument()->insertObj (index + 1, triangles[1]);
+
+ for (LDTriangle* t : triangles)
+ R()->compileObject (t);
+
+ // Delete this quad now, it has been split.
+ obj->deleteSelf();
+
+ num++;
+ }
+
+ log ("%1 quadrilaterals split", num);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (EditRaw, KEY (F9))
+{
+ if (selection().size() != 1)
+ return;
+
+ LDObject* obj = selection()[0];
+ QDialog* dlg = new QDialog;
+ Ui::EditRawUI ui;
+
+ ui.setupUi (dlg);
+ ui.code->setText (obj->raw());
+
+ if (obj->getType() == LDObject::EError)
+ ui.errorDescription->setText (static_cast (obj)->reason);
+ else
+ {
+ ui.errorDescription->hide();
+ ui.errorIcon->hide();
+ }
+
+ if (!dlg->exec())
+ return;
+
+ LDObject* oldobj = obj;
+
+ // Reinterpret it from the text of the input field
+ obj = parseLine (ui.code->text());
+ oldobj->replace (obj);
+
+ // Refresh
+ R()->compileObject (obj);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (SetColor, KEY (C))
+{
+ if (selection().isEmpty())
+ return;
+
+ int colnum;
+ int defcol = -1;
+
+ LDObjectList objs = selection();
+
+ // If all selected objects have the same color, said color is our default
+ // value to the color selection dialog.
+ defcol = getSelectedColor();
+
+ // Show the dialog to the user now and ask for a color.
+ if (ColorSelector::selectColor (colnum, defcol, g_win))
+ {
+ for (LDObject* obj : objs)
+ {
+ if (obj->isColored() == false)
+ continue;
+
+ obj->setColor (colnum);
+ R()->compileObject (obj);
+ }
+
+ refresh();
+ }
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Borders, CTRL_SHIFT (B))
+{
+ LDObjectList objs = selection();
+ int num = 0;
+
+ for (LDObject* obj : objs)
+ {
+ const LDObject::Type type = obj->getType();
+ if (type != LDObject::EQuad && type != LDObject::ETriangle)
+ continue;
+
+ int numLines;
+ LDLine* lines[4];
+
+ if (type == LDObject::EQuad)
+ {
+ numLines = 4;
+
+ LDQuad* quad = static_cast (obj);
+ lines[0] = new LDLine (quad->getVertex (0), quad->getVertex (1));
+ lines[1] = new LDLine (quad->getVertex (1), quad->getVertex (2));
+ lines[2] = new LDLine (quad->getVertex (2), quad->getVertex (3));
+ lines[3] = new LDLine (quad->getVertex (3), quad->getVertex (0));
+ }
+ else
+ {
+ numLines = 3;
+
+ LDTriangle* tri = static_cast (obj);
+ lines[0] = new LDLine (tri->getVertex (0), tri->getVertex (1));
+ lines[1] = new LDLine (tri->getVertex (1), tri->getVertex (2));
+ lines[2] = new LDLine (tri->getVertex (2), tri->getVertex (0));
+ }
+
+ for (int i = 0; i < numLines; ++i)
+ {
+ long idx = obj->getIndex() + i + 1;
+
+ lines[i]->setColor (edgecolor);
+ getCurrentDocument()->insertObj (idx, lines[i]);
+ R()->compileObject (lines[i]);
+ }
+
+ num += numLines;
+ }
+
+ log (tr ("Added %1 border lines"), num);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (CornerVerts, 0)
+{
+ int num = 0;
+
+ for (LDObject* obj : selection())
+ {
+ if (obj->vertices() < 2)
+ continue;
+
+ int idx = obj->getIndex();
+
+ for (int i = 0; i < obj->vertices(); ++i)
+ {
+ LDVertex* vert = new LDVertex;
+ vert->pos = obj->getVertex (i);
+ vert->setColor (obj->getColor());
+
+ getCurrentDocument()->insertObj (++idx, vert);
+ R()->compileObject (vert);
+ ++num;
+ }
+ }
+
+ log (tr ("Added %1 vertices"), num);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+static void doMoveSelection (const bool up)
+{
+ LDObjectList objs = selection();
+ LDObject::moveObjects (objs, up);
+ g_win->buildObjList();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (MoveUp, KEY (PageUp))
+{
+ doMoveSelection (true);
+}
+
+DEFINE_ACTION (MoveDown, KEY (PageDown))
+{
+ doMoveSelection (false);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Undo, CTRL (Z))
+{
+ getCurrentDocument()->undo();
+}
+
+DEFINE_ACTION (Redo, CTRL_SHIFT (Z))
+{
+ getCurrentDocument()->redo();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+void doMoveObjects (Vertex vect)
+{
+ // Apply the grid values
+ vect[X] *= *currentGrid().confs[Grid::X];
+ vect[Y] *= *currentGrid().confs[Grid::Y];
+ vect[Z] *= *currentGrid().confs[Grid::Z];
+
+ for (LDObject* obj : selection())
+ {
+ obj->move (vect);
+ g_win->R()->compileObject (obj);
+ }
+
+ g_win->refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (MoveXNeg, KEY (Left))
+{
+ doMoveObjects ({ -1, 0, 0});
+}
+
+DEFINE_ACTION (MoveYNeg, KEY (Home))
+{
+ doMoveObjects ({0, -1, 0});
+}
+
+DEFINE_ACTION (MoveZNeg, KEY (Down))
+{
+ doMoveObjects ({0, 0, -1});
+}
+
+DEFINE_ACTION (MoveXPos, KEY (Right))
+{
+ doMoveObjects ({1, 0, 0});
+}
+
+DEFINE_ACTION (MoveYPos, KEY (End))
+{
+ doMoveObjects ({0, 1, 0});
+}
+
+DEFINE_ACTION (MoveZPos, KEY (Up))
+{
+ doMoveObjects ({0, 0, 1});
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Invert, CTRL_SHIFT (W))
+{
+ LDObjectList sel = selection();
+
+ for (LDObject* obj : sel)
+ {
+ obj->invert();
+ R()->compileObject (obj);
+ }
+
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+static void rotateVertex (Vertex& v, const Vertex& rotpoint, const Matrix& transform)
+{
+ v.move (-rotpoint);
+ v.transform (transform, g_origin);
+ v.move (rotpoint);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+static void doRotate (const int l, const int m, const int n)
+{
+ LDObjectList sel = selection();
+ QList queue;
+ const Vertex rotpoint = rotPoint (sel);
+ const double angle = (pi * *currentGrid().confs[Grid::Angle]) / 180,
+ cosangle = cos (angle),
+ sinangle = sin (angle);
+
+ // ref: http://en.wikipedia.org/wiki/Transformation_matrix#Rotation_2
+ Matrix transform (
+ {
+ (l* l * (1 - cosangle)) + cosangle,
+ (m* l * (1 - cosangle)) - (n* sinangle),
+ (n* l * (1 - cosangle)) + (m* sinangle),
+
+ (l* m * (1 - cosangle)) + (n* sinangle),
+ (m* m * (1 - cosangle)) + cosangle,
+ (n* m * (1 - cosangle)) - (l* sinangle),
+
+ (l* n * (1 - cosangle)) - (m* sinangle),
+ (m* n * (1 - cosangle)) + (l* sinangle),
+ (n* n * (1 - cosangle)) + cosangle
+ });
+
+ // Apply the above matrix to everything
+ for (LDObject* obj : sel)
+ {
+ if (obj->vertices())
+ {
+ for (int i = 0; i < obj->vertices(); ++i)
+ {
+ Vertex v = obj->getVertex (i);
+ rotateVertex (v, rotpoint, transform);
+ obj->setVertex (i, v);
+ }
+ }
+ elif (obj->hasMatrix())
+ {
+ LDMatrixObject* mo = dynamic_cast (obj);
+
+ // Transform the position
+ Vertex v = mo->getPosition();
+ rotateVertex (v, rotpoint, transform);
+ mo->setPosition (v);
+
+ // Transform the matrix
+ mo->setTransform (transform * mo->getTransform());
+ }
+ elif (obj->getType() == LDObject::EVertex)
+ {
+ LDVertex* vert = static_cast (obj);
+ Vertex v = vert->pos;
+ rotateVertex (v, rotpoint, transform);
+ vert->pos = v;
+ }
+
+ g_win->R()->compileObject (obj);
+ }
+
+ g_win->refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (RotateXPos, CTRL (Right))
+{
+ doRotate (1, 0, 0);
+}
+DEFINE_ACTION (RotateYPos, CTRL (End))
+{
+ doRotate (0, 1, 0);
+}
+DEFINE_ACTION (RotateZPos, CTRL (Up))
+{
+ doRotate (0, 0, 1);
+}
+DEFINE_ACTION (RotateXNeg, CTRL (Left))
+{
+ doRotate (-1, 0, 0);
+}
+DEFINE_ACTION (RotateYNeg, CTRL (Home))
+{
+ doRotate (0, -1, 0);
+}
+DEFINE_ACTION (RotateZNeg, CTRL (Down))
+{
+ doRotate (0, 0, -1);
+}
+
+DEFINE_ACTION (RotationPoint, (0))
+{
+ configRotationPoint();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (RoundCoordinates, 0)
+{
+ setlocale (LC_ALL, "C");
+ int num = 0;
+
+ for (LDObject* obj : selection())
+ {
+ LDMatrixObject* mo = dynamic_cast (obj);
+
+ if (mo != null)
+ {
+ Vertex v = mo->getPosition();
+ Matrix t = mo->getTransform();
+
+ for_axes (ax)
+ roundToDecimals (v[ax], 3);
+
+ // Let matrix values be rounded to 4 decimals,
+ // they need that extra precision
+ for (int i = 0; i < 9; ++i)
+ roundToDecimals (t[i], 4);
+
+ mo->setPosition (v);
+ mo->setTransform (t);
+ num += 10;
+ }
+ else
+ {
+ for (int i = 0; i < obj->vertices(); ++i)
+ {
+ Vertex v = obj->getVertex (i);
+
+ for_axes (ax)
+ roundToDecimals (v[ax], 3);
+
+ obj->setVertex (i, v);
+ R()->compileObject (obj);
+ num += 3;
+ }
+ }
+ }
+
+ log (tr ("Rounded %1 values"), num);
+ refreshObjectList();
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Uncolorize, 0)
+{
+ int num = 0;
+
+ for (LDObject* obj : selection())
+ {
+ if (obj->isColored() == false)
+ continue;
+
+ int col = maincolor;
+
+ if (obj->getType() == LDObject::ELine || obj->getType() == LDObject::ECondLine)
+ col = edgecolor;
+
+ obj->setColor (col);
+ R()->compileObject (obj);
+ num++;
+ }
+
+ log (tr ("%1 objects uncolored"), num);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (ReplaceCoords, CTRL (R))
+{
+ QDialog* dlg = new QDialog (g_win);
+ Ui::ReplaceCoordsUI ui;
+ ui.setupUi (dlg);
+
+ if (!dlg->exec())
+ return;
+
+ const double search = ui.search->value(),
+ replacement = ui.replacement->value();
+ const bool any = ui.any->isChecked(),
+ rel = ui.relative->isChecked();
+
+ QList sel;
+ int num = 0;
+
+ if (ui.x->isChecked()) sel << X;
+ if (ui.y->isChecked()) sel << Y;
+ if (ui.z->isChecked()) sel << Z;
+
+ for (LDObject* obj : selection())
+ {
+ for (int i = 0; i < obj->vertices(); ++i)
+ {
+ Vertex v = obj->getVertex (i);
+
+ for (Axis ax : sel)
+ {
+ double& coord = v[ax];
+
+ if (any || coord == search)
+ {
+ if (!rel)
+ coord = 0;
+
+ coord += replacement;
+ num++;
+ }
+ }
+
+ obj->setVertex (i, v);
+ R()->compileObject (obj);
+ }
+ }
+
+ log (tr ("Altered %1 values"), num);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Flip, CTRL_SHIFT (F))
+{
+ QDialog* dlg = new QDialog;
+ Ui::FlipUI ui;
+ ui.setupUi (dlg);
+
+ if (!dlg->exec())
+ return;
+
+ QList sel;
+
+ if (ui.x->isChecked()) sel << X;
+ if (ui.y->isChecked()) sel << Y;
+ if (ui.z->isChecked()) sel << Z;
+
+ for (LDObject* obj : selection())
+ {
+ for (int i = 0; i < obj->vertices(); ++i)
+ {
+ Vertex v = obj->getVertex (i);
+
+ for (Axis ax : sel)
+ v[ax] *= -1;
+
+ obj->setVertex (i, v);
+ R()->compileObject (obj);
+ }
+ }
+
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Demote, 0)
+{
+ LDObjectList sel = selection();
+ int num = 0;
+
+ for (LDObject* obj : sel)
+ {
+ if (obj->getType() != LDObject::ECondLine)
+ continue;
+
+ LDLine* repl = static_cast (obj)->demote();
+ R()->compileObject (repl);
+ ++num;
+ }
+
+ log (tr ("Demoted %1 conditional lines"), num);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+static bool isColorUsed (int colnum)
+{
+ for (LDObject* obj : getCurrentDocument()->getObjects())
+ if (obj->isColored() && obj->getColor() == colnum)
+ return true;
+
+ return false;
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (Autocolor, 0)
+{
+ int colnum = 0;
+
+ while (colnum < MAX_COLORS && (getColor (colnum) == null || isColorUsed (colnum)))
+ colnum++;
+
+ if (colnum >= MAX_COLORS)
+ {
+ log (tr ("Cannot auto-color: all colors are in use!"));
+ return;
+ }
+
+ for (LDObject* obj : selection())
+ {
+ if (obj->isColored() == false)
+ continue;
+
+ obj->setColor (colnum);
+ R()->compileObject (obj);
+ }
+
+ log (tr ("Auto-colored: new color is [%1] %2"), colnum, getColor (colnum)->name);
+ refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (AddHistoryLine, 0)
+{
+ LDObject* obj;
+ bool ishistory = false,
+ prevIsHistory = false;
+
+ QDialog* dlg = new QDialog;
+ Ui_AddHistoryLine* ui = new Ui_AddHistoryLine;
+ ui->setupUi (dlg);
+ ui->m_username->setText (ld_defaultuser);
+ ui->m_date->setDate (QDate::currentDate());
+ ui->m_comment->setFocus();
+
+ if (!dlg->exec())
+ return;
+
+ // Create the comment object based on input
+ QString commentText = fmt ("!HISTORY %1 [%2] %3",
+ ui->m_date->date().toString ("yyyy-MM-dd"),
+ ui->m_username->text(),
+ ui->m_comment->text());
+
+ LDComment* comm = new LDComment (commentText);
+
+ // Find a spot to place the new comment
+ for (
+ obj = getCurrentDocument()->getObject (0);
+ obj && obj->next() && !obj->next()->isScemantic();
+ obj = obj->next()
+ )
+ {
+ LDComment* comm = dynamic_cast (obj);
+
+ if (comm && comm->text.startsWith ("!HISTORY "))
+ ishistory = true;
+
+ if (prevIsHistory && !ishistory)
+ {
+ // Last line was history, this isn't, thus insert the new history
+ // line here.
+ break;
+ }
+
+ prevIsHistory = ishistory;
+ }
+
+ int idx = obj ? obj->getIndex() : 0;
+ getCurrentDocument()->insertObj (idx++, comm);
+
+ // If we're adding a history line right before a scemantic object, pad it
+ // an empty line
+ if (obj && obj->next() && obj->next()->isScemantic())
+ getCurrentDocument()->insertObj (idx, new LDEmpty);
+
+ buildObjList();
+ delete ui;
+}