mirror of
https://github.com/mapmapteam/mapmap.git
synced 2026-04-04 06:29:41 +02:00
975 lines
29 KiB
C++
975 lines
29 KiB
C++
/*
|
|
* MainWindow.cpp
|
|
*
|
|
* (c) 2013 Sofian Audry -- info(@)sofianaudry(.)com
|
|
* (c) 2013 Alexandre Quessy -- alexandre(@)quessy(.)net
|
|
*
|
|
* 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 "MainWindow.h"
|
|
#include "ProjectWriter.h"
|
|
#include "ProjectReader.h"
|
|
#include "Facade.h"
|
|
#include <sstream>
|
|
|
|
MainWindow* MainWindow::instance = 0;
|
|
|
|
MainWindow::MainWindow()
|
|
{
|
|
mappingManager = new MappingManager;
|
|
// _facade = new Facade(mappingManager, this);
|
|
|
|
currentPaintId = NULL_UID;
|
|
currentMappingId = NULL_UID;
|
|
// TODO: not sure we need this anymore since we have NULL_UID
|
|
_hasCurrentPaint = false;
|
|
_hasCurrentMapping = false;
|
|
|
|
createLayout();
|
|
|
|
createActions();
|
|
createMenus();
|
|
createContextMenu();
|
|
createToolBars();
|
|
createStatusBar();
|
|
|
|
readSettings();
|
|
startOscReceiver();
|
|
|
|
//setWindowIcon(QIcon(":/images/icon.png"));
|
|
setCurrentFile("");
|
|
}
|
|
|
|
MainWindow& MainWindow::getInstance()
|
|
{
|
|
Q_ASSERT(instance);
|
|
|
|
return *instance;
|
|
}
|
|
|
|
void MainWindow::setInstance(MainWindow* inst)
|
|
{
|
|
instance = inst;
|
|
}
|
|
|
|
MainWindow::~MainWindow()
|
|
{
|
|
delete mappingManager;
|
|
// delete _facade;
|
|
}
|
|
|
|
void MainWindow::handlePaintItemSelectionChanged()
|
|
{
|
|
std::cout << "selection changed" << std::endl;
|
|
QListWidgetItem* item = paintList->currentItem();
|
|
uid idx = item->data(Qt::UserRole).toInt();
|
|
std::cout << "idx=" << idx << std::endl;
|
|
setCurrentPaint(idx);
|
|
removeCurrentMapping();
|
|
|
|
// Enable creation of mappings when a paint is selected.
|
|
addQuadAction->setEnabled(true);
|
|
addTriangleAction->setEnabled(true);
|
|
|
|
// Update canvases.
|
|
updateAll();
|
|
//sourceCanvas->switchImage(idx);
|
|
//sourceCanvas->repaint();
|
|
//destinationCanvas->repaint();
|
|
}
|
|
|
|
void MainWindow::handleMappingItemSelectionChanged()
|
|
{
|
|
std::cout << "shape selection changed" << std::endl;
|
|
QListWidgetItem* item = mappingList->currentItem();
|
|
uid idx = item->data(Qt::UserRole).toInt();
|
|
|
|
Mapping::ptr mapping = mappingManager->getMappingById(idx);
|
|
setCurrentPaint(mapping->getPaint()->getId());
|
|
setCurrentMapping(mapping->getId());
|
|
|
|
updateAll();
|
|
//sourceCanvas->switchImage(idx);
|
|
//sourceCanvas->repaint();
|
|
//destinationCanvas->repaint();
|
|
}
|
|
|
|
void MainWindow::handleMappingItemChanged(QListWidgetItem* item)
|
|
{
|
|
uid mappingId = item->data(Qt::UserRole).toInt();
|
|
Mapping::ptr mapping = mappingManager->getMappingById(mappingId);
|
|
mapping->setVisible(item->checkState() == Qt::Checked);
|
|
updateAll();
|
|
}
|
|
|
|
void MainWindow::handleMappingIndexesMoved()
|
|
{
|
|
qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1Moved!!!" << endl;
|
|
std::vector<uid> newOrder;
|
|
for (int row=mappingList->count()-1; row>=0; row--)
|
|
{
|
|
uid layerId = mappingList->item(row)->data(Qt::UserRole).toInt();
|
|
newOrder.push_back(layerId);
|
|
}
|
|
|
|
mappingManager->reorderMappings(newOrder);
|
|
|
|
updateAll();
|
|
}
|
|
|
|
//void MainWindow::handleSourceSelectionChanged(const QItemSelection& selection)
|
|
//{
|
|
// std::cout << "selection changed" << std::endl;
|
|
// QModelIndex& index = selection.indexes().first();
|
|
// int idx = index.row();
|
|
// std::cout << "idx=" << idx << std::endl;
|
|
// sourceCanvas->switchImage(idx);
|
|
//}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
if (okToContinue())
|
|
{
|
|
writeSettings();
|
|
event->accept();
|
|
}
|
|
else
|
|
{
|
|
event->ignore();
|
|
}
|
|
}
|
|
|
|
void MainWindow::newFile()
|
|
{
|
|
if (okToContinue())
|
|
{
|
|
clearWindow();
|
|
setCurrentFile("");
|
|
}
|
|
}
|
|
|
|
void MainWindow::open()
|
|
{
|
|
if (okToContinue())
|
|
{
|
|
QString fileName = QFileDialog::getOpenFileName(this,
|
|
tr("Open project"), ".", tr("LibreMapping files (*.lmp)"));
|
|
if (!fileName.isEmpty())
|
|
loadFile(fileName);
|
|
}
|
|
}
|
|
|
|
bool MainWindow::save()
|
|
{
|
|
if (curFile.isEmpty())
|
|
{
|
|
return saveAs();
|
|
}
|
|
else
|
|
{
|
|
return saveFile(curFile);
|
|
}
|
|
}
|
|
|
|
bool MainWindow::saveAs()
|
|
{
|
|
QString fileName = QFileDialog::getSaveFileName(this, tr("Save project"),
|
|
".", tr("LibreMapping files (*.lmp)"));
|
|
if (fileName.isEmpty())
|
|
return false;
|
|
|
|
return saveFile(fileName);
|
|
}
|
|
|
|
void MainWindow::import()
|
|
{
|
|
if (okToContinue())
|
|
{
|
|
QString fileName = QFileDialog::getOpenFileName(this,
|
|
tr("Import media source file"), ".");
|
|
if (!fileName.isEmpty())
|
|
importMediaFile(fileName);
|
|
}
|
|
}
|
|
|
|
void MainWindow::addMesh()
|
|
{
|
|
// FIXME: crashes if there is no current paint id. (if no paint exists)
|
|
|
|
// Create default quad.
|
|
|
|
// Retrieve current paint (as texture).
|
|
Paint::ptr paint = MainWindow::getInstance().getMappingManager().getPaint(getCurrentPaintId());
|
|
Q_CHECK_PTR(paint);
|
|
|
|
std::tr1::shared_ptr<Texture> texture = std::tr1::static_pointer_cast<Texture>(paint);
|
|
Q_CHECK_PTR(texture);
|
|
|
|
// Create input and output quads.
|
|
qDebug() << "adding mesh" << endl;
|
|
Shape::ptr outputQuad = Shape::ptr(Util::createMeshForTexture(texture.get(), sourceCanvas->width(), sourceCanvas->height()));
|
|
Shape::ptr inputQuad = Shape::ptr(Util::createMeshForTexture(texture.get(), sourceCanvas->width(), sourceCanvas->height()));
|
|
|
|
// Create texture mapping.
|
|
Mapping::ptr mapping(new TextureMapping(paint, outputQuad, inputQuad));
|
|
uint mappingId = mappingManager->addMapping(mapping);
|
|
addMappingItem(mappingId);
|
|
}
|
|
|
|
void MainWindow::addTriangle()
|
|
{
|
|
// A paint must be selected to add a mapping.
|
|
if (getCurrentPaintId() == NULL_UID)
|
|
return;
|
|
|
|
// Create default quad.
|
|
|
|
// Retrieve current paint (as texture).
|
|
Paint::ptr paint = MainWindow::getInstance().getMappingManager().getPaint(getCurrentPaintId());
|
|
Q_CHECK_PTR(paint);
|
|
|
|
std::tr1::shared_ptr<Texture> texture = std::tr1::static_pointer_cast<Texture>(paint);
|
|
Q_CHECK_PTR(texture);
|
|
|
|
// Create input and output quads.
|
|
Shape::ptr outputTriangle = Shape::ptr(Util::createTriangleForTexture(texture.get(), sourceCanvas->width(), sourceCanvas->height()));
|
|
Shape::ptr inputTriangle = Shape::ptr(Util::createTriangleForTexture(texture.get(), sourceCanvas->width(), sourceCanvas->height()));
|
|
|
|
// Create texture mapping.
|
|
Mapping::ptr mapping(new TextureMapping(paint, inputTriangle, outputTriangle));
|
|
uint mappingId = mappingManager->addMapping(mapping);
|
|
addMappingItem(mappingId);
|
|
}
|
|
|
|
void MainWindow::about()
|
|
{
|
|
QMessageBox::about(this, tr("About LibreMapping"),
|
|
tr("<h2>LibreMapping "
|
|
LIBREMAPPING_VERSION
|
|
"</h2>"
|
|
"<p>Copyright © 2013 Sofian Audry"
|
|
"<p>Copyright © 2013 Alexandre Quessy"
|
|
"<p>Copyright © 2013 Vasilis Liaskovitis"
|
|
"<p>LibreMapping is a free software for video mapping. "
|
|
"<p>Projection mapping, also known as video mapping and spatial augmented reality, "
|
|
"is a projection technology used to turn objects, often irregularly shaped, into "
|
|
"a display surface for video projection. These objects may be complex industrial "
|
|
"landscapes, such as buildings. By using specialized software, a two or three "
|
|
"dimensional object is spatially mapped on the virtual program which mimics the "
|
|
"real environment it is to be projected on. The software can interact with a "
|
|
"projector to fit any desired image onto the surface of that object. This "
|
|
"technique is used by artists and advertisers alike who can add extra dimensions, "
|
|
"optical illusions, and notions of movement onto previously static objects. The "
|
|
"video is commonly combined with, or triggered by, audio to create an "
|
|
"audio-visual narrative."
|
|
));
|
|
}
|
|
|
|
void MainWindow::updateStatusBar()
|
|
{
|
|
// TODO
|
|
// locationLabel->setText(spreadsheet->currentLocation());
|
|
// formulaLabel->setText(spreadsheet->currentFormula());
|
|
}
|
|
|
|
bool MainWindow::clearProject()
|
|
{
|
|
// Close property panel.
|
|
propertyPanel->close();
|
|
|
|
// Empty list widgets.
|
|
mappingList->clear();
|
|
paintList->clear();
|
|
|
|
// Clear list of mappers.
|
|
mappers.clear();
|
|
|
|
// Clear model.
|
|
mappingManager->clearAll();
|
|
|
|
return true;
|
|
}
|
|
|
|
uid MainWindow::createImagePaint(uid paintId, QString uri, float x, float y)
|
|
{
|
|
// Cannot create image with already existing id.
|
|
if (Paint::getUidAllocator().exists(paintId))
|
|
return NULL_UID;
|
|
|
|
else
|
|
{
|
|
Image* img = new Image(uri, paintId);
|
|
|
|
// Create new image with corresponding ID.
|
|
img->setPosition(x, y);
|
|
|
|
// Add it to the manager.
|
|
Paint::ptr paint(img);
|
|
|
|
// Add image to paintList widget.
|
|
QListWidgetItem* item = new QListWidgetItem(strippedName(uri));
|
|
item->setData(Qt::UserRole, paint->getId()); // TODO: could possibly be replaced by a Paint pointer
|
|
item->setIcon(QIcon(uri));
|
|
item->setSizeHint(QSize(item->sizeHint().width(), MainWindow::PAINT_LIST_ITEM_HEIGHT));
|
|
paintList->addItem(item);
|
|
paintList->setCurrentItem(item);
|
|
|
|
return mappingManager->addPaint(paint);
|
|
}
|
|
}
|
|
|
|
uid MainWindow::createMeshTextureMapping(uid mappingId,
|
|
uid paintId,
|
|
int nColumns, int nRows,
|
|
const QList<QPointF> &src, const QList<QPointF> &dst)
|
|
{
|
|
// Cannot create element with already existing id or element for which no paint exists.
|
|
if (Mapping::getUidAllocator().exists(mappingId) ||
|
|
!Paint::getUidAllocator().exists(paintId) ||
|
|
paintId == NULL_UID)
|
|
return NULL_UID;
|
|
|
|
else
|
|
{
|
|
Paint::ptr paint = mappingManager->getPaintById(paintId);
|
|
int nVertices = nColumns * nRows;
|
|
qDebug() << nVertices << " vs " << nColumns << "x" << nRows << " vs " << src.size() << " " << dst.size() << endl;
|
|
Q_ASSERT(src.size() == nVertices && dst.size() == nVertices);
|
|
|
|
Shape::ptr inputMesh( new Mesh(src, nColumns, nRows));
|
|
Shape::ptr outputMesh(new Mesh(dst, nColumns, nRows));
|
|
|
|
// Add it to the manager.
|
|
Mapping::ptr mapping(new TextureMapping(paint, outputMesh, inputMesh, mappingId));
|
|
uid id = mappingManager->addMapping(mapping);
|
|
|
|
// Add it to the GUI.
|
|
addMappingItem(mappingId);
|
|
|
|
// Return the id.
|
|
return id;
|
|
}
|
|
}
|
|
|
|
uid MainWindow::createTriangleTextureMapping(uid mappingId,
|
|
uid paintId,
|
|
const QList<QPointF> &src, const QList<QPointF> &dst)
|
|
{
|
|
// Cannot create element with already existing id or element for which no paint exists.
|
|
if (Mapping::getUidAllocator().exists(mappingId) ||
|
|
!Paint::getUidAllocator().exists(paintId) ||
|
|
paintId == NULL_UID)
|
|
return NULL_UID;
|
|
|
|
else
|
|
{
|
|
Paint::ptr paint = mappingManager->getPaintById(paintId);
|
|
Q_ASSERT(src.size() == 3 && dst.size() == 3);
|
|
|
|
Shape::ptr inputTriangle( new Triangle(src[0], src[1], src[2]));
|
|
Shape::ptr outputTriangle(new Triangle(dst[0], dst[1], dst[2]));
|
|
|
|
// Add it to the manager.
|
|
Mapping::ptr mapping(new TextureMapping(paint, outputTriangle, inputTriangle, mappingId));
|
|
uid id = mappingManager->addMapping(mapping);
|
|
|
|
// Add it to the GUI.
|
|
addMappingItem(mappingId);
|
|
|
|
// Return the id.
|
|
return id;
|
|
}
|
|
}
|
|
|
|
void MainWindow::windowModified()
|
|
{
|
|
setWindowModified(true);
|
|
updateStatusBar();
|
|
}
|
|
|
|
void MainWindow::createLayout()
|
|
{
|
|
paintList = new QListWidget;
|
|
paintList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
paintList->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
paintList->setMinimumWidth(PAINT_LIST_MINIMUM_WIDTH);
|
|
|
|
mappingList = new QListWidget;
|
|
mappingList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
mappingList->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
//layerList->setDragDropMode(QAbstractItemView::DragDrop);
|
|
mappingList->setDefaultDropAction(Qt::MoveAction);
|
|
mappingList->setDragDropMode(QAbstractItemView::InternalMove);
|
|
mappingList->setMinimumWidth(MAPPING_LIST_MINIMUM_WIDTH);
|
|
|
|
propertyPanel = new QStackedWidget;
|
|
propertyPanel->setDisabled(true);
|
|
propertyPanel->setMinimumWidth(PROPERTY_PANEL_MINIMUM_WIDTH);
|
|
|
|
sourceCanvas = new SourceGLCanvas;
|
|
destinationCanvas = new DestinationGLCanvas(0, sourceCanvas);
|
|
|
|
connect(sourceCanvas, SIGNAL(shapeChanged(Shape*)),
|
|
destinationCanvas, SLOT(updateCanvas()));
|
|
|
|
// connect(destinationCanvas, SIGNAL(imageChanged()),
|
|
// sourceCanvas, SLOT(updateCanvas()));
|
|
|
|
sourceCanvas->setFocusPolicy(Qt::ClickFocus);
|
|
destinationCanvas->setFocusPolicy(Qt::ClickFocus);
|
|
|
|
sourceCanvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
destinationCanvas->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
|
|
sourceCanvas->setMinimumSize(CANVAS_MINIMUM_WIDTH, CANVAS_MINIMUM_HEIGHT);
|
|
destinationCanvas->setMinimumSize(CANVAS_MINIMUM_WIDTH, CANVAS_MINIMUM_HEIGHT);
|
|
|
|
mainSplitter = new QSplitter(Qt::Vertical);
|
|
|
|
resourceSplitter = new QSplitter(Qt::Horizontal);
|
|
resourceSplitter->addWidget(paintList);
|
|
resourceSplitter->addWidget(mappingList);
|
|
resourceSplitter->addWidget(propertyPanel);
|
|
|
|
canvasSplitter = new QSplitter(Qt::Horizontal);
|
|
canvasSplitter->addWidget(sourceCanvas);
|
|
canvasSplitter->addWidget(destinationCanvas);
|
|
|
|
mainSplitter->addWidget(canvasSplitter);
|
|
mainSplitter->addWidget(resourceSplitter);
|
|
|
|
// Initialize size to 2:1 proportions.
|
|
QSize sz = mainSplitter->size();
|
|
QList<int> sizes;
|
|
sizes.append(sz.height() * 2 / 3);
|
|
sizes.append(sz.height() - sizes.at(0));
|
|
mainSplitter->setSizes(sizes);
|
|
|
|
// Upon resizing window, give some extra stretch expansion to canvasSplitter.
|
|
//mainSplitter->setStretchFactor(0, 1);
|
|
|
|
setWindowTitle(tr("LibreMapping"));
|
|
resize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
setCentralWidget(mainSplitter);
|
|
|
|
// Common::initializeLibremapper(sourceCanvas->width(), sourceCanvas->height());
|
|
//
|
|
// for (int i = 0; i < Common::nImages(); i++)
|
|
// {
|
|
// std::tr1::shared_ptr<Image> img = std::tr1::static_pointer_cast<Image>(
|
|
// Common::mappings[i]->getPaint());
|
|
// Q_CHECK_PTR(img);
|
|
//
|
|
// QListWidgetItem* item = new QListWidgetItem(strippedName(img->getImagePath()));
|
|
// item->setData(Qt::UserRole, i);
|
|
//
|
|
// sourceList->addItem(item);
|
|
// }
|
|
|
|
connect(paintList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(handlePaintItemSelectionChanged()));
|
|
|
|
connect(mappingList, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(handleMappingItemSelectionChanged()));
|
|
|
|
connect(mappingList, SIGNAL(itemChanged(QListWidgetItem*)),
|
|
this, SLOT(handleMappingItemChanged(QListWidgetItem*)));
|
|
|
|
connect(mappingList->model(), SIGNAL(layoutChanged()),
|
|
this, SLOT(handleMappingIndexesMoved()));
|
|
|
|
|
|
// sourceList->setModel(sourcesModel);
|
|
//
|
|
// connect(sourceList->selectionModel(),
|
|
// SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
|
// this, SLOT(handleSourceSelectionChanged(QItemSelection)));
|
|
}
|
|
|
|
void MainWindow::createActions()
|
|
{
|
|
newAction = new QAction(tr("&New"), this);
|
|
newAction->setIcon(QIcon(":/images/document-new-4.png"));
|
|
newAction->setShortcut(QKeySequence::New);
|
|
newAction->setStatusTip(tr("Create a new project"));
|
|
connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));
|
|
|
|
openAction = new QAction(tr("&Open..."), this);
|
|
openAction->setIcon(QIcon(":/images/document-open-3.png"));
|
|
openAction->setShortcut(QKeySequence::Open);
|
|
openAction->setStatusTip(tr("Open an existing project"));
|
|
connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
|
|
|
|
saveAction = new QAction(tr("&Save"), this);
|
|
saveAction->setIcon(QIcon(":/images/document-save-2.png"));
|
|
saveAction->setShortcut(QKeySequence::Save);
|
|
saveAction->setStatusTip(tr("Save the project"));
|
|
connect(saveAction, SIGNAL(triggered()), this, SLOT(save()));
|
|
|
|
saveAsAction = new QAction(tr("Save &As..."), this);
|
|
saveAsAction->setIcon(QIcon(":/images/document-save-as-2.png"));
|
|
saveAsAction->setStatusTip(tr("Save the project as..."));
|
|
connect(saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs()));
|
|
|
|
importAction = new QAction(tr("&Import media source file..."), this);
|
|
importAction->setIcon(QIcon(":/images/document-import-2.png"));
|
|
importAction->setShortcut(QKeySequence::Open);
|
|
importAction->setStatusTip(tr("Import a media source file..."));
|
|
connect(importAction, SIGNAL(triggered()), this, SLOT(import()));
|
|
|
|
exitAction = new QAction(tr("E&xit"), this);
|
|
exitAction->setShortcut(tr("Ctrl+Q"));
|
|
exitAction->setStatusTip(tr("Exit the application"));
|
|
connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
|
|
|
|
// cutAction = new QAction(tr("Cu&t"), this);
|
|
// cutAction->setIcon(QIcon(":/images/cut.png"));
|
|
// cutAction->setShortcut(QKeySequence::Cut);
|
|
// cutAction->setStatusTip(tr("Cut the current selection's contents to the clipboard"));
|
|
// connect(cutAction, SIGNAL(triggered()), spreadsheet, SLOT(cut()));
|
|
//
|
|
// copyAction = new QAction(tr("&Copy"), this);
|
|
// copyAction->setIcon(QIcon(":/images/copy.png"));
|
|
// copyAction->setShortcut(QKeySequence::Copy);
|
|
// copyAction->setStatusTip(tr("Copy the current selection's contents to the clipboard"));
|
|
// connect(copyAction, SIGNAL(triggered()), spreadsheet, SLOT(copy()));
|
|
//
|
|
// pasteAction = new QAction(tr("&Paste"), this);
|
|
// pasteAction->setIcon(QIcon(":/images/paste.png"));
|
|
// pasteAction->setShortcut(QKeySequence::Paste);
|
|
// pasteAction->setStatusTip(tr("Paste the clipboard's contents into the current selection"));
|
|
// connect(pasteAction, SIGNAL(triggered()), spreadsheet, SLOT(paste()));
|
|
//
|
|
// deleteAction = new QAction(tr("&Delete"), this);
|
|
// deleteAction->setShortcut(QKeySequence::Delete);
|
|
// deleteAction->setStatusTip(tr("Delete the current selection's contents"));
|
|
// connect(deleteAction, SIGNAL(triggered()), spreadsheet, SLOT(del()));
|
|
|
|
aboutAction = new QAction(tr("&About"), this);
|
|
aboutAction->setStatusTip(tr("Show the application's About box"));
|
|
connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
|
|
|
|
addQuadAction = new QAction(tr("&Add quad"), this);
|
|
addQuadAction->setIcon(QIcon(":/images/draw-rectangle-2.png"));
|
|
addQuadAction->setStatusTip(tr("Add quad"));
|
|
connect(addQuadAction, SIGNAL(triggered()), this, SLOT(addMesh()));
|
|
addQuadAction->setEnabled(false);
|
|
|
|
addTriangleAction = new QAction(tr("&Add triangle"), this);
|
|
addTriangleAction->setIcon(QIcon(":/images/draw-triangle.png"));
|
|
addTriangleAction->setStatusTip(tr("Add triangle"));
|
|
connect(addTriangleAction, SIGNAL(triggered()), this, SLOT(addTriangle()));
|
|
addTriangleAction->setEnabled(false);
|
|
}
|
|
|
|
void MainWindow::createMenus()
|
|
{
|
|
fileMenu = menuBar()->addMenu(tr("&File"));
|
|
fileMenu->addAction(newAction);
|
|
fileMenu->addAction(openAction);
|
|
fileMenu->addAction(importAction);
|
|
fileMenu->addAction(saveAction);
|
|
fileMenu->addAction(saveAsAction);
|
|
separatorAction = fileMenu->addSeparator();
|
|
fileMenu->addAction(exitAction);
|
|
|
|
// editMenu = menuBar()->addMenu(tr("&Edit"));
|
|
// editMenu->addAction(cutAction);
|
|
// editMenu->addAction(copyAction);
|
|
// editMenu->addAction(pasteAction);
|
|
// editMenu->addAction(deleteAction);
|
|
|
|
// selectSubMenu = editMenu->addMenu(tr("&Select"));
|
|
// selectSubMenu->addAction(selectRowAction);
|
|
// selectSubMenu->addAction(selectColumnAction);
|
|
// selectSubMenu->addAction(selectAllAction);
|
|
|
|
// editMenu->addSeparator();
|
|
// editMenu->addAction(findAction);
|
|
// editMenu->addAction(goToCellAction);
|
|
|
|
// toolsMenu = menuBar()->addMenu(tr("&Tools"));
|
|
// toolsMenu->addAction(recalculateAction);
|
|
// toolsMenu->addAction(sortAction);
|
|
//
|
|
// optionsMenu = menuBar()->addMenu(tr("&Options"));
|
|
// optionsMenu->addAction(showGridAction);
|
|
// optionsMenu->addAction(autoRecalcAction);
|
|
|
|
menuBar()->addSeparator();
|
|
|
|
helpMenu = menuBar()->addMenu(tr("&Help"));
|
|
helpMenu->addAction(aboutAction);
|
|
// helpMenu->addAction(aboutQtAction);
|
|
}
|
|
|
|
void MainWindow::createContextMenu()
|
|
{
|
|
// spreadsheet->addAction(cutAction);
|
|
// spreadsheet->addAction(copyAction);
|
|
// spreadsheet->addAction(pasteAction);
|
|
// spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu);
|
|
}
|
|
|
|
void MainWindow::createToolBars()
|
|
{
|
|
fileToolBar = addToolBar(tr("&File"));
|
|
fileToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
|
|
fileToolBar->addAction(importAction);
|
|
fileToolBar->addAction(newAction);
|
|
fileToolBar->addAction(openAction);
|
|
fileToolBar->addAction(saveAction);
|
|
fileToolBar->addSeparator();
|
|
fileToolBar->addAction(addQuadAction);
|
|
fileToolBar->addAction(addTriangleAction);
|
|
|
|
// editToolBar = addToolBar(tr("&Edit"));
|
|
// editToolBar->addAction(cutAction);
|
|
// editToolBar->addAction(copyAction);
|
|
// editToolBar->addAction(pasteAction);
|
|
// editToolBar->addSeparator();
|
|
// editToolBar->addAction(findAction);
|
|
// editToolBar->addAction(goToCellAction);
|
|
}
|
|
|
|
void MainWindow::createStatusBar()
|
|
{
|
|
// locationLabel = new QLabel(" W999 ");
|
|
// locationLabel->setAlignment(Qt::AlignHCenter);
|
|
// locationLabel->setMinimumSize(locationLabel->sizeHint());
|
|
//
|
|
// formulaLabel = new QLabel;
|
|
// formulaLabel->setIndent(3);
|
|
//
|
|
// statusBar()->addWidget(locationLabel);
|
|
// statusBar()->addWidget(formulaLabel, 1);
|
|
//
|
|
// connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)), this,
|
|
// SLOT(updateStatusBar()));
|
|
// connect(spreadsheet, SIGNAL(modified()), this, SLOT(spreadsheetModified()));
|
|
|
|
updateStatusBar();
|
|
}
|
|
|
|
void MainWindow::readSettings()
|
|
{
|
|
QSettings settings("LibreMapping", "LibreMapping");
|
|
|
|
restoreGeometry(settings.value("geometry").toByteArray());
|
|
mainSplitter->restoreState(settings.value("mainSplitter").toByteArray());
|
|
resourceSplitter->restoreState(settings.value("resourceSplitter").toByteArray());
|
|
canvasSplitter->restoreState(settings.value("canvasSplitter").toByteArray());
|
|
config_osc_receive_port = settings.value("osc_receive_port", 12345).toInt();
|
|
}
|
|
|
|
void MainWindow::writeSettings()
|
|
{
|
|
QSettings settings("LibreMapping", "LibreMapping");
|
|
|
|
settings.setValue("geometry", saveGeometry());
|
|
settings.setValue("mainSplitter", mainSplitter->saveState());
|
|
settings.setValue("resourceSplitter", resourceSplitter->saveState());
|
|
settings.setValue("canvasSplitter", canvasSplitter->saveState());
|
|
settings.setValue("osc_receive_port", config_osc_receive_port);
|
|
}
|
|
|
|
bool MainWindow::okToContinue()
|
|
{
|
|
if (isWindowModified())
|
|
{
|
|
int r = QMessageBox::warning(this, tr("LibreMapping"),
|
|
tr("The document has been modified.\n"
|
|
"Do you want to save your changes?"),
|
|
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
|
if (r == QMessageBox::Yes)
|
|
{
|
|
return save();
|
|
}
|
|
else if (r == QMessageBox::Cancel)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MainWindow::loadFile(const QString &fileName)
|
|
{
|
|
QFile file(fileName);
|
|
|
|
if (! file.open(QFile::ReadOnly | QFile::Text))
|
|
{
|
|
QMessageBox::warning(this, tr("Error reading mapping project file"),
|
|
tr("Cannot read file %1:\n%2.")
|
|
.arg(fileName)
|
|
.arg(file.errorString()));
|
|
return false;
|
|
}
|
|
|
|
// Clear current project.
|
|
clearProject();
|
|
|
|
// Read new project
|
|
ProjectReader reader(this);
|
|
if (! reader.readFile(&file))
|
|
{
|
|
QMessageBox::warning(this, tr("Error reading mapping project file"),
|
|
tr("Parse error in file %1:\n\n%2")
|
|
.arg(fileName)
|
|
.arg(reader.errorString()));
|
|
}
|
|
else
|
|
{
|
|
statusBar()->showMessage(tr("File loaded"), 2000);
|
|
setCurrentFile(fileName);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MainWindow::saveFile(const QString &fileName)
|
|
{
|
|
QFile file(fileName);
|
|
if (! file.open(QFile::WriteOnly | QFile::Text))
|
|
{
|
|
QMessageBox::warning(this, tr("Error saving mapping project"),
|
|
tr("Cannot write file %1:\n%2.")
|
|
.arg(fileName)
|
|
.arg(file.errorString()));
|
|
return false;
|
|
}
|
|
|
|
ProjectWriter writer(mappingManager);
|
|
if (writer.writeFile(&file))
|
|
{
|
|
setCurrentFile(fileName);
|
|
statusBar()->showMessage(tr("File saved"), 2000);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void MainWindow::setCurrentFile(const QString &fileName)
|
|
{
|
|
curFile = fileName;
|
|
setWindowModified(false);
|
|
|
|
QString shownName = tr("Untitled");
|
|
if (!curFile.isEmpty())
|
|
{
|
|
shownName = strippedName(curFile);
|
|
}
|
|
|
|
setWindowTitle(tr("%1[*] - %2").arg(shownName).arg(tr("LibreMapping Project")));
|
|
}
|
|
|
|
// TODO
|
|
// bool MainWindow::updateMediaFile(const QString &source_name, const QString &fileName)
|
|
// {
|
|
// }
|
|
|
|
bool MainWindow::importMediaFile(const QString &fileName)
|
|
{
|
|
QFile file(fileName);
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
QMessageBox::warning(this, tr("LibreMapping Project"),
|
|
tr("Cannot read file %1:\n%2.")
|
|
.arg(file.fileName())
|
|
.arg(file.errorString()));
|
|
return false;
|
|
}
|
|
|
|
QApplication::setOverrideCursor(Qt::WaitCursor);
|
|
|
|
// Add image to model.
|
|
uint imageId = createImagePaint(NULL_UID, fileName, 0, 0);
|
|
|
|
// Initialize position (center).
|
|
std::tr1::shared_ptr<Image> image = std::tr1::static_pointer_cast<Image>(mappingManager->getPaintById(imageId));
|
|
Q_CHECK_PTR(image);
|
|
|
|
image->setPosition((sourceCanvas->width() - image->getWidth() ) / 2.0f,
|
|
(sourceCanvas->height() - image->getHeight()) / 2.0f );
|
|
|
|
|
|
// update();
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
statusBar()->showMessage(tr("File imported"), 2000);
|
|
return true;
|
|
}
|
|
|
|
void MainWindow::addMappingItem(uint mappingId)
|
|
{
|
|
Mapping::ptr mapping = mappingManager->getMappingById(mappingId);
|
|
Q_CHECK_PTR(mapping);
|
|
|
|
QString label;
|
|
QIcon icon;
|
|
|
|
// Add mapper.
|
|
// XXX hardcoded for textures
|
|
std::tr1::shared_ptr<TextureMapping> textureMapping = std::tr1::static_pointer_cast<TextureMapping>(mapping);
|
|
Q_CHECK_PTR(textureMapping);
|
|
|
|
Mapper::ptr mapper;
|
|
|
|
// XXX Branching on nVertices() is crap
|
|
|
|
// Triangle
|
|
if (mapping->getShape()->getType() == "triangle")
|
|
{
|
|
label = QString("Triangle %1").arg(mappingId);
|
|
icon = QIcon(":/images/draw-triangle.png");
|
|
mapper = Mapper::ptr(new TriangleTextureMapper(textureMapping));
|
|
}
|
|
// Mesh
|
|
else if (mapping->getShape()->getType() == "mesh")
|
|
{
|
|
label = QString("Quad %1").arg(mappingId);
|
|
icon = QIcon(":/images/draw-rectangle-2.png");
|
|
mapper = Mapper::ptr(new MeshTextureMapper(textureMapping));
|
|
}
|
|
else
|
|
{
|
|
label = QString("Polygon %1").arg(mappingId);
|
|
icon = QIcon(":/images/draw-polygon-2.png");
|
|
}
|
|
|
|
// Add to list of mappers.
|
|
mappers[mappingId] = mapper;
|
|
QWidget* mapperEditor = mapper->getPropertiesEditor();
|
|
propertyPanel->addWidget(mapperEditor);
|
|
propertyPanel->setCurrentWidget(mapperEditor);
|
|
propertyPanel->setEnabled(true);
|
|
|
|
// When mapper value is changed, update canvases.
|
|
connect(mapper.get(), SIGNAL(valueChanged()),
|
|
this, SLOT(updateAll()));
|
|
|
|
connect(sourceCanvas, SIGNAL(shapeChanged(Shape*)),
|
|
mapper.get(), SLOT(updateShape(Shape*)));
|
|
|
|
connect(destinationCanvas, SIGNAL(shapeChanged(Shape*)),
|
|
mapper.get(), SLOT(updateShape(Shape*)));
|
|
|
|
// Add item to layerList widget.
|
|
QListWidgetItem* item = new QListWidgetItem(label);
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Checked);
|
|
item->setData(Qt::UserRole, mappingId); // TODO: could possibly be replaced by a Paint pointer
|
|
item->setIcon(icon);
|
|
item->setSizeHint(QSize(item->sizeHint().width(), MainWindow::SHAPE_LIST_ITEM_HEIGHT));
|
|
mappingList->insertItem(0, item);
|
|
mappingList->setCurrentItem(item);
|
|
}
|
|
|
|
void MainWindow::clearWindow()
|
|
{
|
|
clearProject();
|
|
}
|
|
|
|
void MainWindow::updateAll()
|
|
{
|
|
sourceCanvas->update();
|
|
destinationCanvas->update();
|
|
}
|
|
|
|
QString MainWindow::strippedName(const QString &fullFileName)
|
|
{
|
|
return QFileInfo(fullFileName).fileName();
|
|
}
|
|
|
|
void MainWindow::startOscReceiver()
|
|
{
|
|
#ifdef HAVE_OSC
|
|
int port = config_osc_receive_port;
|
|
std::ostringstream os;
|
|
os << port;
|
|
osc_interface.reset(new OscInterface(os.str()));
|
|
if (port != 0)
|
|
{
|
|
osc_interface->start();
|
|
}
|
|
osc_timer = new QTimer(this); // FIXME: memleak?
|
|
connect(osc_timer, SIGNAL(timeout()), this, SLOT(pollOscInterface()));
|
|
osc_timer->start();
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::pollOscInterface()
|
|
{
|
|
#ifdef HAVE_OSC
|
|
osc_interface->consume_commands(*_facade);
|
|
#endif
|
|
}
|
|
|
|
void MainWindow::applyOscCommand(QVariantList & command)
|
|
{
|
|
bool VERBOSE = true;
|
|
if (VERBOSE)
|
|
{
|
|
std::cout << "Receive OSC: ";
|
|
for (int i = 0; i < command.size(); ++i)
|
|
{
|
|
if (command.at(i).type() == QVariant::Int)
|
|
{
|
|
std::cout << command.at(i).toInt() << " ";
|
|
}
|
|
else if (command.at(i).type() == QVariant::Double)
|
|
{
|
|
std::cout << command.at(i).toDouble() << " ";
|
|
}
|
|
else if (command.at(i).type() == QVariant::String)
|
|
{
|
|
std::cout << command.at(i).toString().toStdString() << " ";
|
|
}
|
|
else
|
|
{
|
|
std::cout << "??? ";
|
|
}
|
|
}
|
|
std::cout << std::endl;
|
|
std::cout.flush();
|
|
}
|
|
|
|
if (command.size() < 2)
|
|
return;
|
|
if (command.at(0).type() != QVariant::String)
|
|
return;
|
|
if (command.at(1).type() != QVariant::String)
|
|
return;
|
|
std::string path = command.at(0).toString().toStdString();
|
|
std::string typetags = command.at(1).toString().toStdString();
|
|
|
|
// Handle all OSC messages here
|
|
if (path == "/image/uri" && typetags == "s")
|
|
{
|
|
std::string image_uri = command.at(2).toString().toStdString();
|
|
std::cout << "TODO load /image/uri " << image_uri << std::endl;
|
|
}
|
|
else if (path == "/add/quad")
|
|
addMesh();
|
|
else if (path == "/add/triangle")
|
|
addTriangle();
|
|
else if (path == "/project/save")
|
|
save();
|
|
else if (path == "/project/open")
|
|
open();
|
|
}
|
|
|