Split inverting into methods into LDObject and its subclasses, fixed a rather severe memory leak issue (GL display lists weren't being removed properly)

Sun, 12 May 2013 20:21:44 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Sun, 12 May 2013 20:21:44 +0300
changeset 192
c414924a647c
parent 191
9bb6a17305ad
child 193
ce8e25ccbaf6

Split inverting into methods into LDObject and its subclasses, fixed a rather severe memory leak issue (GL display lists weren't being removed properly)

src/addObjectDialog.cpp file | annotate | diff | comparison | revisions
src/configDialog.cpp file | annotate | diff | comparison | revisions
src/extprogs.cpp file | annotate | diff | comparison | revisions
src/file.cpp file | annotate | diff | comparison | revisions
src/gldraw.cpp file | annotate | diff | comparison | revisions
src/gui.cpp file | annotate | diff | comparison | revisions
src/gui.h file | annotate | diff | comparison | revisions
src/gui_actions.cpp file | annotate | diff | comparison | revisions
src/gui_editactions.cpp file | annotate | diff | comparison | revisions
src/history.cpp file | annotate | diff | comparison | revisions
src/history.h file | annotate | diff | comparison | revisions
src/ldtypes.cpp file | annotate | diff | comparison | revisions
src/ldtypes.h file | annotate | diff | comparison | revisions
src/newPartDialog.cpp file | annotate | diff | comparison | revisions
src/setContentsDialog.cpp file | annotate | diff | comparison | revisions
--- a/src/addObjectDialog.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/addObjectDialog.cpp	Sun May 12 20:21:44 2013 +0300
@@ -507,5 +507,5 @@
 		History::addEntry (new EditHistory ({(ulong) obj->getIndex (g_curfile)}, {backup}, {obj->clone ()}));
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
\ No newline at end of file
--- a/src/configDialog.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/configDialog.cpp	Sun May 12 20:21:44 2013 +0300
@@ -709,7 +709,7 @@
 		reloadAllSubfiles ();
 		
 		g_win->R ()->setBackground ();
-		g_win->refresh ();
+		g_win->fullRefresh ();
 		g_win->updateToolBars ();
 	}
 }
--- a/src/extprogs.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/extprogs.cpp	Sun May 12 20:21:44 2013 +0300
@@ -198,7 +198,7 @@
 		return;
 	}
 	
-	ComboHistory* cmb = new ComboHistory ({});
+	ComboHistory* cmb = new ComboHistory ();
 	std::vector<LDObject*> objs = loadFileContents (fp, null),
 		copies;
 	std::vector<ulong> indices;
@@ -233,7 +233,7 @@
 		delete cmb;
 	
 	fclose (fp);
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
--- a/src/file.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/file.cpp	Sun May 12 20:21:44 2013 +0300
@@ -284,7 +284,7 @@
 	g_loadedFiles.clear();
 	g_curfile = NULL;
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -300,7 +300,7 @@
 	g_curfile = f;
 	
 	g_BBox.calculate();
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -362,7 +362,7 @@
 	g_BBox.calculate();
 	
 	// Rebuild the object tree view now.
-	g_win->refresh ();
+	g_win->fullRefresh ();
 	g_win->setTitle ();
 	g_win->R ()->resetAngles ();
 	
--- a/src/gldraw.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/gldraw.cpp	Sun May 12 20:21:44 2013 +0300
@@ -1132,7 +1132,7 @@
 		if (obj) {
 			g_curfile->addObject (obj);
 			compileObject (obj);
-			g_win->refresh ();
+			g_win->fullRefresh ();
 			
 			History::addEntry (new AddHistory ({(ulong) obj->getIndex (g_curfile)}, {obj->clone ()}));
 		}
@@ -1154,6 +1154,8 @@
 // =============================================================================
 void GLRenderer::compileObject (LDObject* obj) {
 	for (const GL::ListType listType : g_glListTypes) {
+		glDeleteLists (obj->glLists[listType], 1);
+		
 		GLuint list = glGenLists (1);
 		glNewList (list, GL_COMPILE);
 		
--- a/src/gui.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/gui.cpp	Sun May 12 20:21:44 2013 +0300
@@ -189,7 +189,6 @@
 	addMenuAction ("paste");				// Paste
 	addMenuAction ("del");					// Delete
 	menu->addSeparator ();					// -----
-	
 	addMenuAction ("selectAll");			// Select All
 	addMenuAction ("selectByColor");		// Select by Color
 	addMenuAction ("selectByType");		// Select by Type
@@ -207,6 +206,8 @@
 	addMenuAction ("roundCoords");		// Round Coordinates
 	addMenuAction ("uncolorize");			// Uncolorize
 	addMenuAction ("visibility");			// Toggle Visibility
+	addMenuAction ("replaceCoords");		// Replace Coordinates
+	addMenuAction ("flip");				// Flip
 	
 	// Move menu
 	initMenu ("&Move");
@@ -377,6 +378,7 @@
 	addToolBarAction ("uncolorize");
 	addToolBarAction ("visibility");
 	addToolBarAction ("replaceCoords");
+	addToolBarAction ("flip");
 	
 	initSingleToolBar ("External Programs");
 	addToolBarAction ("ytruder");
@@ -524,7 +526,7 @@
 		delete obj;
 	}
 	
-	refresh ();
+	fullRefresh ();
 }
 
 // =============================================================================
@@ -539,6 +541,9 @@
 	// while this is done.
 	g_bSelectionLocked = true;
 	
+	for (int i = 0; i < m_objList->count (); ++i)
+		delete m_objList->item (i);
+	
 	m_objList->clear ();
 	
 	for (LDObject* obj : g_curfile->m_objs) {
@@ -770,7 +775,7 @@
 	}
 	
 	History::addEntry (new SetColorHistory (indices, colors, newColor));
-	refresh ();
+	fullRefresh ();
 }
 
 // =============================================================================
@@ -789,11 +794,16 @@
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
-void ForgeWindow::refresh () {
+void ForgeWindow::fullRefresh () {
 	buildObjList ();
 	m_renderer->hardRefresh ();
 }
 
+void ForgeWindow::update() {
+	buildObjList ();
+	m_renderer->update ();
+}
+
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
--- a/src/gui.h	Sat May 11 04:02:13 2013 +0300
+++ b/src/gui.h	Sun May 12 20:21:44 2013 +0300
@@ -128,7 +128,8 @@
 	ForgeWindow ();
 	void buildObjList ();
 	void setTitle ();
-	void refresh ();
+	void fullRefresh ();
+	void update ();
 	ulong getInsertionPoint ();
 	void deleteSelection (vector<ulong>* ulapIndices, std::vector<LDObject*>* papObjects);
 	void updateToolBars ();
--- a/src/gui_actions.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/gui_actions.cpp	Sun May 12 20:21:44 2013 +0300
@@ -312,7 +312,7 @@
 	
 	if (historyCopies.size() > 0) {
 		History::addEntry (new AddHistory (historyIndices, historyCopies));
-		g_win->refresh ();
+		g_win->fullRefresh ();
 		g_win->scrollToSelection ();
 	}
 }
@@ -354,7 +354,7 @@
 	
 	if (historyCopies.size () > 0) {
 		History::addEntry (new AddHistory (historyIndices, historyCopies));
-		g_win->refresh ();
+		g_win->fullRefresh ();
 		g_win->scrollToSelection ();
 	}
 }
@@ -415,5 +415,5 @@
 	for (LDObject* obj : g_win->sel ())
 		obj->setHidden (!obj->hidden ());
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
\ No newline at end of file
--- a/src/gui_editactions.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/gui_editactions.cpp	Sun May 12 20:21:44 2013 +0300
@@ -101,7 +101,7 @@
 	}
 	
 	History::addEntry (new AddHistory (historyIndices, historyCopies, AddHistory::Paste));
-	g_win->refresh ();
+	g_win->fullRefresh ();
 	g_win->scrollToSelection ();
 }
 
@@ -168,7 +168,7 @@
 	}
 	
 	History::addEntry (new InlineHistory (bitIndices, refIndices, refs, bDeep));
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 MAKE_ACTION (inlineContents, "Inline", "inline", "Inline selected subfiles.", CTRL (I)) {
@@ -228,7 +228,7 @@
 	}
 	
 	History::addEntry (history);
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =======================================================================================================================================
@@ -271,7 +271,7 @@
 	}
 	
 	History::addEntry (new QuadSplitHistory (ulaIndices, paCopies));
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -316,7 +316,7 @@
 		}
 		
 		History::addEntry (new SetColorHistory (ulaIndices, daColors, dColor));
-		g_win->refresh ();
+		g_win->fullRefresh ();
 	}
 }
 
@@ -367,7 +367,7 @@
 	}
 	
 	History::addEntry (new AddHistory (ulaIndices, paObjs));
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -397,7 +397,7 @@
 	
 	if (ulaIndices.size() > 0) {
 		History::addEntry (new AddHistory (ulaIndices, paObjs));
-		g_win->refresh ();
+		g_win->fullRefresh ();
 	}
 }
 
@@ -458,7 +458,7 @@
 	}
 	
 	History::addEntry (new MoveHistory (ulaIndices, vVector));
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 MAKE_ACTION (moveXNeg, "Move -X", "move-x-neg", "Move selected objects negative on the X axis.", KEY (Left)) {
@@ -498,116 +498,20 @@
 // History.
 // =============================================================================
 MAKE_ACTION (invert, "Invert", "invert", "Reverse the winding of given objects.", CTRL_SHIFT (W)) {
-	std::vector<LDObject*> paSelection = g_win->sel ();
-	std::vector<HistoryEntry*> paHistory;
+	std::vector<LDObject*> sel = g_win->sel ();
+	ComboHistory* history = new ComboHistory;
 	
-	for (LDObject* obj : paSelection) {
-		// For the objects we end up editing, we store information into these
-		// variables and we store them into an EditHistory after the switch
-		// block. Subfile and radial management is stored into the history
-		// list immediately.
-		ulong ulHistoryIndex = obj->getIndex (g_curfile);
-		LDObject* pOldCopy, *pNewCopy;
-		bool bEdited = false;
-		
-		switch (obj->getType ()) {
-		case LDObject::Line:
-		case LDObject::CondLine:
-			{
-				// For lines, we swap the vertices. I don't think that a
-				// cond-line's control points need to be swapped, do they?
-				LDLine* pLine = static_cast<LDLine*> (obj);
-				vertex vTemp = pLine->coords[0];
-				
-				pOldCopy = pLine->clone ();
-				pLine->coords[0] = pLine->coords[1];
-				pLine->coords[1] = vTemp;
-				pNewCopy = pLine->clone ();
-				bEdited = true;
-			}
-			break;
-		
-		case LDObject::Triangle:
-			{
-				// Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1.
-				// Thus, we swap 1 and 2.
-				LDTriangle* pTri = static_cast<LDTriangle*> (obj);
-				vertex vTemp = pTri->coords[1];
-				
-				pOldCopy = pTri->clone ();
-				pTri->coords[1] = pTri->coords[2];
-				pTri->coords[2] = vTemp;
-				pNewCopy = pTri->clone ();
-				bEdited = true;
-			}
-			break;
-		
-		case LDObject::Quad:
-			{
-				// Quad: 0 -> 1 -> 2 -> 3
-				// rev:  0 -> 3 -> 2 -> 1
-				// Thus, we swap 1 and 3.
-				LDQuad* pQuad = static_cast<LDQuad*> (obj);
-				vertex vTemp = pQuad->coords[1];
-				
-				pOldCopy = pQuad->clone ();
-				pQuad->coords[1] = pQuad->coords[3];
-				pQuad->coords[3] = vTemp;
-				pNewCopy = pQuad->clone ();
-				bEdited = true;
-			}
-			break;
-		
-		case LDObject::Subfile:
-		case LDObject::Radial:
-			{
-				// Subfiles and radials are inverted when they're prefixed with
-				// a BFC INVERTNEXT statement. Thus we need to toggle this status.
-				// For flat primitives it's sufficient that the determinant is
-				// flipped but I don't have a method for checking flatness yet.
-				// Food for thought...
-				
-				bool inverted = false;
-				ulong idx = obj->getIndex (g_curfile);
-				
-				if (idx > 0) {
-					LDObject* prev = g_curfile->object (idx - 1);
-					LDBFC* bfc = dynamic_cast<LDBFC*> (prev);
-					
-					if (bfc && bfc->type == LDBFC::InvertNext) {
-						// Object is prefixed with an invertnext, thus remove it.
-						paHistory.push_back (new DelHistory ({idx - 1}, {bfc->clone ()}));
-						
-						inverted = true;
-						g_curfile->forgetObject (bfc);
-						delete bfc;
-					}
-				}
-				
-				if (!inverted) {
-					// Not inverted, thus prefix it with a new invertnext.
-					LDBFC* bfc = new LDBFC (LDBFC::InvertNext);
-					g_curfile->insertObj (idx, bfc);
-					
-					paHistory.push_back (new AddHistory ({idx}, {bfc->clone ()}));
-				}
-			}
-			break;
-		
-		default:
-			break;
-		}
-		
-		// If we edited this object, store the EditHistory based on collected
-		// information now.
-		if (bEdited == true)
-			paHistory.push_back (new EditHistory ({ulHistoryIndex}, {pOldCopy}, {pNewCopy}));
+	for (LDObject* obj : sel) {
+		*history << obj->invert ();
+		g_win->R ()->compileObject (obj);
 	}
 	
-	if (paHistory.size () > 0) {
-		History::addEntry (new ComboHistory (paHistory));
-		g_win->refresh ();
-	}
+	printf ("%lu entries\n", history->numEntries ());
+	if (history->numEntries () > 0) {
+		History::addEntry (history);
+		g_win->buildObjList ();
+	} else
+		delete history;
 }
 
 // =============================================================================
@@ -672,7 +576,7 @@
 		v->move (origin);
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 MAKE_ACTION (rotateXPos, "Rotate +X", "rotate-x-pos", "Rotate objects around X axis", CTRL (Right)) {
@@ -710,7 +614,7 @@
 	for (const Axis ax : g_Axes)
 		obj->coords[i][ax] = atof (fmt ("%.3f", obj->coords[i][ax]));
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -733,7 +637,7 @@
 	
 	if (indices.size () > 0) {
 		History::addEntry (new EditHistory (indices, oldCopies, newCopies));
-		g_win->refresh ();
+		g_win->fullRefresh ();
 	}
 }
 
@@ -755,13 +659,18 @@
 // =========================================================================================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =========================================================================================================================================
+static CheckBoxGroup<Axis>* makeAxesBox () {
+	CheckBoxGroup<Axis>* cbg_axes = new CheckBoxGroup<Axis> ("Axes", Qt::Horizontal);
+	cbg_axes->addCheckBox ("X", X);
+	cbg_axes->addCheckBox ("Y", Y);
+	cbg_axes->addCheckBox ("Z", Z);
+	return cbg_axes;
+}
+
 class ReplaceCoordsDialog : public QDialog {
 public:
 	explicit ReplaceCoordsDialog (QWidget* parent = null, Qt::WindowFlags f = 0) : QDialog (parent, f) {
-		cbg_axes = new CheckBoxGroup<Axis> ("Axes", Qt::Horizontal);
-		cbg_axes->addCheckBox ("X", X);
-		cbg_axes->addCheckBox ("Y", Y);
-		cbg_axes->addCheckBox ("Z", Z);
+		cbg_axes = makeAxesBox ();
 		
 		lb_search = new QLabel ("Search:");
 		lb_replacement = new QLabel ("Replacement:");
@@ -813,17 +722,63 @@
 	EditHistory* history = new EditHistory;
 	
 	for (LDObject* obj : g_win->sel ()) {
-		bool altered = false;
 		LDObject* copy = obj->clone ();
 		
 		for (short i = 0; i < obj->vertices (); ++i)
 		for (Axis ax : sel) {
 			if (obj->coords[i][ax] == search) {
 				obj->coords[i][ax] = replacement;
-				altered = true;
 			}
 		}
 		
+		history->addEntry (copy, obj, obj->getIndex (g_curfile));
+		
+		delete copy;
+	}
+	
+	History::addEntry (history);
+	g_win->fullRefresh ();
+}
+
+// =========================================================================================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =========================================================================================================================================
+class FlipDialog : public QDialog {
+public:
+	explicit FlipDialog (QWidget* parent = 0, Qt::WindowFlags f = 0) : QDialog (parent, f) {
+		cbg_axes = makeAxesBox ();
+		
+		QVBoxLayout* layout = new QVBoxLayout;
+		layout->addWidget (cbg_axes);
+		layout->addWidget (makeButtonBox (*this));
+		setLayout (layout);
+	}
+	
+	vector<Axis> axes () { return cbg_axes->checkedValues (); }
+	
+private:
+	CheckBoxGroup<Axis>* cbg_axes;
+};
+
+MAKE_ACTION (flip, "Flip", "flip", "Flip coordinates", CTRL_SHIFT (F)) {
+	FlipDialog dlg;
+	
+	if (!dlg.exec ())
+		return;
+	
+	EditHistory* history = new EditHistory;
+	vector<Axis> sel = dlg.axes ();
+	
+	for (LDObject* obj : g_win->sel ()) {
+		bool altered = false;
+		LDObject* copy = obj->clone ();
+		
+		for (short i = 0; i < obj->vertices (); ++i)
+		for (Axis ax : sel) {
+			obj->coords[i][ax] *= -1;
+			altered = true;
+		}
+		
 		if (altered)
 			history->addEntry (copy, obj, obj->getIndex (g_curfile));
 		
@@ -831,5 +786,5 @@
 	}
 	
 	History::addEntry (history);
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
\ No newline at end of file
--- a/src/history.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/history.cpp	Sun May 12 20:21:44 2013 +0300
@@ -105,7 +105,7 @@
 		g_curfile->insertObj (indices[idx], obj);
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -117,7 +117,7 @@
 		delete obj;
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -134,7 +134,7 @@
 	for (ulong i = 0; i < ulaIndices.size (); ++i)
 		g_curfile->m_objs[ulaIndices[i]]->color = daColors[i];
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 void SetColorHistory::redo () {
@@ -142,7 +142,7 @@
 	for (ulong i = 0; i < ulaIndices.size (); ++i)
 		g_curfile->m_objs[ulaIndices[i]]->color = dNewColor;
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 SetColorHistory::~SetColorHistory () {}
@@ -154,14 +154,14 @@
 	for (ulong idx : ulaIndices)
 		g_curfile->object (idx)->replace (paOldObjs[idx]->clone ());
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 void EditHistory::redo () {
 	for (ulong idx : ulaIndices)
 		g_curfile->object (idx)->replace (paNewObjs[idx]->clone ());
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 void EditHistory::addEntry (LDObject* const oldObj, LDObject* const newObj) {
@@ -175,10 +175,11 @@
 }
 
 EditHistory::~EditHistory () {
-	for (ulong idx : ulaIndices) {
-		delete paOldObjs[idx];
-		delete paNewObjs[idx];
-	}
+	for (LDObject* obj : paOldObjs)
+		delete obj;
+	
+	for (LDObject* obj : paNewObjs)
+		delete obj;
 }
 
 // =============================================================================
@@ -224,7 +225,7 @@
 		delete obj;
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 void AddHistory::redo () {
@@ -235,7 +236,7 @@
 		g_curfile->insertObj (idx, obj);
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -262,7 +263,7 @@
 		delete tri2;
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 void QuadSplitHistory::redo () {
@@ -277,7 +278,7 @@
 		delete pQuad;
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
@@ -295,7 +296,7 @@
 		g_curfile->insertObj (ulaRefIndices[i], obj);
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 void InlineHistory::redo () {
@@ -313,7 +314,7 @@
 		delete ref;
 	}
 	
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 InlineHistory::~InlineHistory () {
@@ -331,13 +332,13 @@
 	
 	for (ulong i : ulaIndices)
 		g_curfile->object (i)->move (vInverse);
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 void MoveHistory::redo () {
 	for (ulong i : ulaIndices)
 		g_curfile->object (i)->move (vVector);
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
 
 // =============================================================================
--- a/src/history.h	Sat May 11 04:02:13 2013 +0300
+++ b/src/history.h	Sun May 12 20:21:44 2013 +0300
@@ -102,8 +102,9 @@
 		std::vector<LDObject*> paNewObjs) :
 		ulaIndices (ulaIndices), paOldObjs (paOldObjs), paNewObjs (paNewObjs) {}
 	
-	void addEntry (LDObject* const oldObj, LDObject* const newObj);
-	void addEntry (LDObject* const oldObj, LDObject* const newObj, const ulong idx);
+	void	addEntry		(LDObject* const oldObj, LDObject* const newObj);
+	void	addEntry		(LDObject* const oldObj, LDObject* const newObj, const ulong idx);
+	ulong	numEntries		() const { return ulaIndices.size (); }
 };
 
 // =============================================================================
@@ -195,12 +196,12 @@
 	
 	std::vector<HistoryEntry*> paEntries;
 	
+	ComboHistory () {}
 	ComboHistory (std::vector<HistoryEntry*> paEntries) : paEntries (paEntries) {}
 	
-	ComboHistory& operator<< (HistoryEntry* entry) {
-		paEntries.push_back (entry);
-		return *this;
-	}
+	void			addEntry		(HistoryEntry* entry) { if (entry) paEntries.push_back (entry); }
+	ulong			numEntries		() const { return paEntries.size (); }
+	ComboHistory&	operator<<		(HistoryEntry* entry) { addEntry (entry); return *this;}
 };
 
 // =============================================================================
--- a/src/ldtypes.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/ldtypes.cpp	Sun May 12 20:21:44 2013 +0300
@@ -21,6 +21,7 @@
 #include "file.h"
 #include "misc.h"
 #include "gui.h"
+#include "history.h"
 
 char const* g_saObjTypeNames[] = {
 	"subfile",
@@ -694,4 +695,98 @@
 	CHECK_FOR_OBJ (Gibberish)
 	CHECK_FOR_OBJ (Vertex)
 	return null;
-}
\ No newline at end of file
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+HistoryEntry* LDObject::invert () { return null; }
+HistoryEntry* LDBFC::invert () { return null; }
+HistoryEntry* LDEmpty::invert () { return null; }
+HistoryEntry* LDComment::invert () { return null; }
+HistoryEntry* LDGibberish::invert () { return null; }
+
+HistoryEntry* LDTriangle::invert () {
+	// Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1.
+	// Thus, we swap 1 and 2.
+	vertex tmp = coords[1];
+	
+	LDObject* oldCopy = clone ();
+	coords[1] = coords[2];
+	coords[2] = tmp;
+	
+	return new EditHistory ({(ulong) getIndex (g_curfile)}, {oldCopy}, {clone ()});
+}
+
+HistoryEntry* LDQuad::invert () {
+	// Quad: 0 -> 1 -> 2 -> 3
+	// rev:  0 -> 3 -> 2 -> 1
+	// Thus, we swap 1 and 3.
+	vertex tmp = coords[1];
+	LDObject* oldCopy = clone ();
+	
+	coords[1] = coords[3];
+	coords[3] = tmp;
+	
+	return new EditHistory ({(ulong) getIndex (g_curfile)}, {oldCopy}, {clone ()});
+}
+
+static HistoryEntry* invertSubfile (LDObject* obj) {
+	// Subfiles and radials are inverted when they're prefixed with
+	// a BFC INVERTNEXT statement. Thus we need to toggle this status.
+	// For flat primitives it's sufficient that the determinant is
+	// flipped but I don't have a method for checking flatness yet.
+	// Food for thought...
+	
+	ulong idx = obj->getIndex (g_curfile);
+	
+	if (idx > 0) {
+		LDBFC* bfc = dynamic_cast<LDBFC*> (obj->prev ());
+		
+		if (bfc && bfc->type == LDBFC::InvertNext) {
+			// Object is prefixed with an invertnext, thus remove it.
+			HistoryEntry* history = new DelHistory ({idx - 1}, {bfc->clone ()});
+			
+			g_curfile->forgetObject (bfc);
+			delete bfc;
+			return history;
+		}
+	}
+	
+	// Not inverted, thus prefix it with a new invertnext.
+	LDBFC* bfc = new LDBFC (LDBFC::InvertNext);
+	g_curfile->insertObj (idx, bfc);
+	
+	return new AddHistory ({idx}, {bfc->clone ()});
+}
+
+HistoryEntry* LDSubfile::invert () {
+	return invertSubfile (this);
+}
+
+HistoryEntry* LDRadial::invert () {
+	return invertSubfile (this);
+}
+
+static HistoryEntry* invertLine (LDObject* line) {
+	// For lines, we swap the vertices. I don't think that a
+	// cond-line's control points need to be swapped, do they?
+	LDObject* oldCopy = line->clone ();
+	vertex tmp = line->coords[0];
+	
+	oldCopy = line->clone ();
+	line->coords[0] = line->coords[1];
+	line->coords[1] = tmp;
+	
+	return new EditHistory ({(ulong) line->getIndex (g_curfile)}, {oldCopy}, {line->clone ()});
+}
+
+HistoryEntry* LDLine::invert () {
+	return invertLine (this);
+}
+
+HistoryEntry* LDCondLine::invert () {
+	return invertLine (this);
+}
+
+HistoryEntry* LDVertex::invert () { return null; }
\ No newline at end of file
--- a/src/ldtypes.h	Sat May 11 04:02:13 2013 +0300
+++ b/src/ldtypes.h	Sun May 12 20:21:44 2013 +0300
@@ -22,6 +22,8 @@
 #include "common.h"
 #include "types.h"
 
+class HistoryEntry;
+
 #define IMPLEMENT_LDTYPE(T, NUMVERTS) \
 	LD##T () {} \
 	virtual ~LD##T () {} \
@@ -34,6 +36,7 @@
 	} \
 	virtual void move (vertex vVector); \
 	virtual short vertices () const { return NUMVERTS; } \
+	virtual HistoryEntry* invert ();
 
 #define LDOBJ_SETCOLORED(V) virtual bool isColored () const { return V; }
 #define LDOBJ_COLORED LDOBJ_SETCOLORED (true)
@@ -138,12 +141,13 @@
 	// Object list entry for this object
 	QListWidgetItem* qObjListEntry;
 	
-	bool		hidden			() const { return m_hidden; }
-	LDObject*	next			() const;
-	LDObject*	prev			() const;
-	void		setHidden		(const bool hidden) { m_hidden = hidden; }
-	bool		selected		() const { return m_selected; }
-	void		setSelected	(bool selected) { m_selected = selected; }
+	bool					hidden			() const { return m_hidden; }
+	virtual HistoryEntry*	invert			();
+	LDObject*				next			() const;
+	LDObject*				prev			() const;
+	void					setHidden		(const bool hidden) { m_hidden = hidden; }
+	bool					selected		() const { return m_selected; }
+	void					setSelected	(bool selected) { m_selected = selected; }
 
 private:
 	bool m_hidden;
--- a/src/newPartDialog.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/newPartDialog.cpp	Sun May 12 20:21:44 2013 +0300
@@ -116,6 +116,6 @@
 		objs.push_back (new LDBFC (eBFCType));
 		objs.push_back (new LDEmpty);
 		
-		g_win->refresh ();
+		g_win->fullRefresh ();
 	}
 }
\ No newline at end of file
--- a/src/setContentsDialog.cpp	Sat May 11 04:02:13 2013 +0300
+++ b/src/setContentsDialog.cpp	Sun May 12 20:21:44 2013 +0300
@@ -103,5 +103,5 @@
 	oldobj->replace (obj);
 	
 	// Rebuild stuff after this
-	g_win->refresh ();
+	g_win->fullRefresh ();
 }
\ No newline at end of file

mercurial