gui.cpp

Wed, 10 Apr 2013 00:46:32 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Wed, 10 Apr 2013 00:46:32 +0300
changeset 85
b1541b547c8c
parent 84
c9438ea54ed9
child 87
8dbf0fdf3374
permissions
-rw-r--r--

Added undo/redo foundations. Capable of undoing and redoing delete operations

/*
 *  LDForge: LDraw parts authoring CAD
 *  Copyright (C) 2013 Santeri `arezey` 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 <http://www.gnu.org/licenses/>.
 */

#include <QtGui>

#include "common.h"
#include "gldraw.h"
#include "gui.h"
#include "file.h"
#include "config.h"
#include "misc.h"
#include "colors.h"
#include "config.h"

EXTERN_ACTION (newFile)
EXTERN_ACTION (open)
EXTERN_ACTION (save)
EXTERN_ACTION (saveAs)
EXTERN_ACTION (settings)
EXTERN_ACTION (exit)
EXTERN_ACTION (cut)
EXTERN_ACTION (copy)
EXTERN_ACTION (paste)
EXTERN_ACTION (del)
EXTERN_ACTION (setColor)
EXTERN_ACTION (inlineContents)
EXTERN_ACTION (deepInline)
EXTERN_ACTION (splitQuads)
EXTERN_ACTION (setContents)
EXTERN_ACTION (makeBorders)
EXTERN_ACTION (moveUp)
EXTERN_ACTION (moveDown)
EXTERN_ACTION (newSubfile)
EXTERN_ACTION (newLine)
EXTERN_ACTION (newCondLine)
EXTERN_ACTION (newTriangle)
EXTERN_ACTION (newQuad)
EXTERN_ACTION (newVertex)
EXTERN_ACTION (newComment)
EXTERN_ACTION (help)
EXTERN_ACTION (about)
EXTERN_ACTION (aboutQt)
EXTERN_ACTION (undo)
EXTERN_ACTION (redo)

vector<actionmeta> g_ActionMeta;
cfg (bool, lv_colorize, true);

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
ForgeWindow::ForgeWindow () {
	R = new renderer;
	
	qObjList = new QTreeWidget;
	qObjList->setHeaderHidden (true);
	qObjList->setMaximumWidth (256);
	qObjList->setSelectionMode (QTreeWidget::MultiSelection);
	connect (qObjList, SIGNAL (itemSelectionChanged ()), this, SLOT (slot_selectionChanged ()));
	
	qMessageLog = new QTextEdit;
	qMessageLog->setReadOnly (true);
	qMessageLog->setMaximumHeight (96);
	
	QWidget* w = new QWidget;
	QGridLayout* layout = new QGridLayout;
	layout->setColumnMinimumWidth (0, 192);
	layout->setColumnStretch (0, 1);
	layout->addWidget (R,			0, 0);
	layout->addWidget (qObjList,	0, 1);
	layout->addWidget (qMessageLog,	1, 0, 1, 2);
	w->setLayout (layout);
	setCentralWidget (w);
	
	createMenuActions ();
	createMenus ();
	createToolbars ();
	
	slot_selectionChanged ();
	
	setWindowIcon (QIcon ("icons/ldforge.png"));
	setTitle ();
	setMinimumSize (320, 200);
	resize (800, 600);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ForgeWindow::createMenuActions () {
	// Create the actions based on stored meta.
	for (actionmeta meta : g_ActionMeta) {
		QAction*& qAct = *meta.qAct;
		qAct = new QAction (getIcon (meta.sIconName), meta.sDisplayName, this);
		qAct->setStatusTip (meta.sDescription);
		qAct->setShortcut (*meta.conf);
		
		connect (qAct, SIGNAL (triggered ()), this, SLOT (slot_action ()));
	}
	
	// things not implemented yet
	QAction* const qaDisabledActions[] = {
		ACTION_NAME (newSubfile),
		ACTION_NAME (about),
		ACTION_NAME (help),
	};
	
	for (QAction* act : qaDisabledActions)
		act->setEnabled (false);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
#define ADD_MENU_ITEM(MENU, ACT) q##MENU##Menu->addAction (ACTION_NAME (ACT));

void ForgeWindow::createMenus () {
	// File menu
	qFileMenu = menuBar ()->addMenu (tr ("&File"));
	ADD_MENU_ITEM (File, newFile)			// New
	ADD_MENU_ITEM (File, open)				// Open
	ADD_MENU_ITEM (File, save)				// Save
	ADD_MENU_ITEM (File, saveAs)			// Save As
	qFileMenu->addSeparator ();				// -------
	ADD_MENU_ITEM (File, settings)			// Settings
	qFileMenu->addSeparator ();				// -------
	ADD_MENU_ITEM (File, exit)				// Exit
	
	// Insert menu
	qInsertMenu = menuBar ()->addMenu (tr ("&Insert"));
	ADD_MENU_ITEM (Insert, newSubfile)		// New Subfile
	ADD_MENU_ITEM (Insert, newLine)			// New Line
	ADD_MENU_ITEM (Insert, newTriangle)		// New Triangle
	ADD_MENU_ITEM (Insert, newQuad)			// New Quad
	ADD_MENU_ITEM (Insert, newCondLine)		// New Conditional Line
	ADD_MENU_ITEM (Insert, newComment)		// New Comment
	ADD_MENU_ITEM (Insert, newVertex)		// New Vertex
	
	// Edit menu
	qEditMenu = menuBar ()->addMenu (tr ("&Edit"));
	ADD_MENU_ITEM (Edit, undo)				// Undo
	ADD_MENU_ITEM (Edit, redo)				// Redo
	qEditMenu->addSeparator ();				// -----
	ADD_MENU_ITEM (Edit, cut)				// Cut
	ADD_MENU_ITEM (Edit, copy)				// Copy
	ADD_MENU_ITEM (Edit, paste)				// Paste
	ADD_MENU_ITEM (Edit, del)				// Delete
	qEditMenu->addSeparator ();				// -----
	ADD_MENU_ITEM (Edit, moveUp)			// Move Up
	ADD_MENU_ITEM (Edit, moveDown)			// Move Down
	qEditMenu->addSeparator ();				// -----
	ADD_MENU_ITEM (Edit, setColor)			// Set Color
	qEditMenu->addSeparator ();				// -----
	ADD_MENU_ITEM (Edit, inlineContents)	// Inline
	ADD_MENU_ITEM (Edit, deepInline)		// Deep Inline
	ADD_MENU_ITEM (Edit, splitQuads)		// Split Quads
	ADD_MENU_ITEM (Edit, setContents)		// Set Contents
	ADD_MENU_ITEM (Edit, makeBorders)		// Make Borders
	
	// Help menu
	qHelpMenu = menuBar ()->addMenu (tr ("&Help"));
	ADD_MENU_ITEM (Help, help)				// Help
	qHelpMenu->addSeparator ();				// -----
	ADD_MENU_ITEM (Help, about)				// About
	ADD_MENU_ITEM (Help, aboutQt)			// About Qt
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
#define ADD_TOOLBAR_ITEM(BAR, ACT) q##BAR##ToolBar->addAction (ACTION_NAME (ACT));

void ForgeWindow::createToolbars () {
	qFileToolBar = new QToolBar ("File");
	ADD_TOOLBAR_ITEM (File, newFile)
	ADD_TOOLBAR_ITEM (File, open)
	ADD_TOOLBAR_ITEM (File, save)
	ADD_TOOLBAR_ITEM (File, saveAs)
	addToolBar (qFileToolBar);
	
	qInsertToolBar = new QToolBar ("Insert");
	ADD_TOOLBAR_ITEM (Insert, newSubfile)
	ADD_TOOLBAR_ITEM (Insert, newLine)
	ADD_TOOLBAR_ITEM (Insert, newTriangle)
	ADD_TOOLBAR_ITEM (Insert, newQuad)
	ADD_TOOLBAR_ITEM (Insert, newCondLine)
	ADD_TOOLBAR_ITEM (Insert, newComment)
	ADD_TOOLBAR_ITEM (Insert, newVertex)
	addToolBar (qInsertToolBar);
	
	qEditToolBar = new QToolBar ("Edit");
	ADD_TOOLBAR_ITEM (Edit, undo)
	ADD_TOOLBAR_ITEM (Edit, redo)
	ADD_TOOLBAR_ITEM (Edit, cut)
	ADD_TOOLBAR_ITEM (Edit, copy)
	ADD_TOOLBAR_ITEM (Edit, paste)
	ADD_TOOLBAR_ITEM (Edit, del)
	ADD_TOOLBAR_ITEM (Edit, moveUp)
	ADD_TOOLBAR_ITEM (Edit, moveDown)
	ADD_TOOLBAR_ITEM (Edit, setColor)
	ADD_TOOLBAR_ITEM (Edit, inlineContents)
	ADD_TOOLBAR_ITEM (Edit, splitQuads)
	ADD_TOOLBAR_ITEM (Edit, setContents)
	ADD_TOOLBAR_ITEM (Edit, makeBorders)
	addToolBar (qEditToolBar);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ForgeWindow::setTitle () {
	str zTitle = APPNAME_DISPLAY " v" VERSION_STRING;
	
	// Append our current file if we have one
	if (g_CurrentFile) {
		zTitle.appendformat (": %s", basename (g_CurrentFile->zFileName.chars()));
		
		if (g_CurrentFile->objects.size() > 0 &&
			g_CurrentFile->objects[0]->getType() == OBJ_Comment)
		{
			// Append title
			LDComment* comm = static_cast<LDComment*> (g_CurrentFile->objects[0]);
			zTitle.appendformat (":%s", comm->zText.chars());
		}
	}
	
	setWindowTitle (zTitle.chars());
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ForgeWindow::slot_action () {
	// Get the action that triggered this slot.
	QAction* qAct = static_cast<QAction*> (sender ());
	
	// Find the meta for the action.
	actionmeta* pMeta = nullptr;
	
	for (actionmeta meta : g_ActionMeta) {
		if (*meta.qAct == qAct) {
			pMeta = &meta;
			break;
		}
	}
	
	if (!pMeta) {
		fprintf (stderr, "unknown signal sender %p!\n", qAct);
		return;
	}
	
	// We have the meta, now call the handler.
	(*pMeta->handler) ();
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ForgeWindow::deleteSelection () {
	vector<LDObject*> objs = getSelectedObjects ();
	
	// Delete the objects that were being selected
	for (LDObject* obj : objs) {
		g_CurrentFile->forgetObject (obj);
		delete obj;
	}
	
	if (objs.size() > 0)
		refresh ();
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ForgeWindow::buildObjList () {
	if (!g_CurrentFile)
		return;
	
	QList<QTreeWidgetItem*> qaItems;
	
	qObjList->clear ();
	
	for (LDObject* obj : g_CurrentFile->objects) {
		str zText;
		switch (obj->getType ()) {
		case OBJ_Comment:
			zText = static_cast<LDComment*> (obj)->zText.chars();
			
			// Remove leading whitespace
			while (~zText && zText[0] == ' ')
				zText -= -1;
			break;
		
		case OBJ_Empty:
			break; // leave it empty
		
		case OBJ_Line:
			{
				LDLine* line = static_cast<LDLine*> (obj);
				zText.format ("%s, %s",
					line->vaCoords[0].getStringRep (true).chars(),
					line->vaCoords[1].getStringRep (true).chars());
			}
			break;
		
		case OBJ_Triangle:
			{
				LDTriangle* triangle = static_cast<LDTriangle*> (obj);
				zText.format ("%s, %s, %s",
					triangle->vaCoords[0].getStringRep (true).chars(),
					triangle->vaCoords[1].getStringRep (true).chars(),
					triangle->vaCoords[2].getStringRep (true).chars());
			}
			break;
		
		case OBJ_Quad:
			{
				LDQuad* quad = static_cast<LDQuad*> (obj);
				zText.format ("%s, %s, %s, %s",
					quad->vaCoords[0].getStringRep (true).chars(),
					quad->vaCoords[1].getStringRep (true).chars(),
					quad->vaCoords[2].getStringRep (true).chars(),
					quad->vaCoords[3].getStringRep (true).chars());
			}
			break;
		
		case OBJ_CondLine:
			{
				LDCondLine* line = static_cast<LDCondLine*> (obj);
				zText.format ("%s, %s, %s, %s",
					line->vaCoords[0].getStringRep (true).chars(),
					line->vaCoords[1].getStringRep (true).chars(),
					line->vaCoords[2].getStringRep (true).chars(),
					line->vaCoords[3].getStringRep (true).chars());
			}
			break;
		
		case OBJ_Gibberish:
			zText.format ("ERROR: %s",
				static_cast<LDGibberish*> (obj)->zContents.chars());
			break;
		
		case OBJ_Vertex:
			zText.format ("%s", static_cast<LDVertex*> (obj)->vPosition.getStringRep (true).chars());
			break;
		
		case OBJ_Subfile:
			{
				LDSubfile* ref = static_cast<LDSubfile*> (obj);
				
				zText.format ("%s %s, (",
					ref->zFileName.chars(), ref->vPosition.getStringRep (true).chars());
				
				for (short i = 0; i < 9; ++i)
					zText.appendformat ("%s%s",
						ftoa (ref->mMatrix[i]).chars(),
						(i != 8) ? " " : "");
				
				zText += ')';
			}
			break;
		
		case OBJ_BFC:
			{
				LDBFC* bfc = static_cast<LDBFC*> (obj);
				zText = LDBFC::saStatements[bfc->dStatement];
			}
			break;
		
		default:
			zText = g_saObjTypeNames[obj->getType ()];
			break;
		}
		
		QTreeWidgetItem* item = new QTreeWidgetItem ((QTreeWidget*) (nullptr),
			QStringList (zText.chars()), 0);
		item->setIcon (0, QIcon (str::mkfmt ("icons/%s.png", g_saObjTypeIcons[obj->getType ()]).chars()));
		
		// Color gibberish red
		if (obj->getType() == OBJ_Gibberish) {
			item->setBackground (0, QColor ("#AA0000"));
			item->setForeground (0, QColor ("#FFAA00"));
		} else if (lv_colorize &&
			obj->dColor != -1 &&
			obj->dColor != dMainColor &&
			obj->dColor != dEdgeColor)
		{
			// If the object isn't in the main or edge color, draw this
			// list entry in said color.
			color* col = getColor (obj->dColor);
			if (col)
				item->setForeground (0, col->qColor);
		}
		
		obj->qObjListEntry = item;
		
		qaItems.append (item);
	}
	
	qObjList->insertTopLevelItems (0, qaItems);
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ForgeWindow::slot_selectionChanged () {
	/*
	// If the selection isn't 1 exact, disable setting contents
	ACTION (setContents)->setEnabled (qObjList->selectedItems().size() == 1);
	
	// If we have no selection, disable splitting quads
	ACTION (splitQuads)->setEnabled (qObjList->selectedItems().size() > 0);
	*/
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
ulong ForgeWindow::getInsertionPoint () {
	ulong ulIndex;
	
	if (qObjList->selectedItems().size() == 1) {
		// If we have a selection, put the item after it.
		for (ulIndex = 0; ulIndex < g_CurrentFile->objects.size(); ++ulIndex)
			if (g_CurrentFile->objects[ulIndex]->qObjListEntry == qObjList->selectedItems()[0])
				break;
		
		if (ulIndex >= g_CurrentFile->objects.size())
			return ulIndex + 1;
	}
	
	// Otherwise place the object at the end.
	return g_CurrentFile->objects.size();
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
void ForgeWindow::refresh () {
	buildObjList ();
	R->hardRefresh ();
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
std::vector<LDObject*> ForgeWindow::getSelectedObjects () {
	std::vector<LDObject*> objs;
	
	QList<QTreeWidgetItem*> const qaItems = qObjList->selectedItems();
	for (LDObject* obj : g_CurrentFile->objects)
	for (QTreeWidgetItem* qItem : qaItems) {
		if (qItem == obj->qObjListEntry) {
			objs.push_back (obj);
			break;
		}
	}
	
	return objs;
}

// =============================================================================
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// =============================================================================
QIcon getIcon (const char* sIconName) {
	return (QIcon (str::mkfmt ("./icons/%s.png", sIconName)));
}

mercurial