Beispiel #1
0
void GraphicalModelView::rowsInserted(const QModelIndex &parent, int start, int end)
{
	const QPersistentModelIndex parentIndex = parent.sibling(parent.row(), 0);
	Id parentLogicalId = parentIndex.data(roles::logicalIdRole).value<Id>();

	for (int row = start; row <= end; ++row) {
		const QPersistentModelIndex current = model()->index(row, 0, parent);
		const Id logicalId = current.data(roles::logicalIdRole).value<Id>();
		if (parentLogicalId.isNull() || parentLogicalId.editor() != "MetaEditor"
				|| logicalId.editor() != "MetaEditor")
		{
			parentLogicalId = Id::rootId();
		}

		const QString name = current.data(Qt::DisplayRole).toString();
		if (logicalId.isNull()) {
			// No logical Id for this item, so logical model shouldn't care
			// about it.
			continue;
		}

		// Add this element to a root for now. To be able to do something
		// useful, we need to establish a correspondence between logical
		// and graphical model hierarchy. It is not always easy since
		// some elements have no correspondences in another model, and tree
		// structures may be very different by themselves.
		LogicalModel * const logicalModel = static_cast<LogicalModel *>(mModel);

		const bool isEdge = mModel->editorManagerInterface().isNodeOrEdge(logicalId.editor(), logicalId.element());

		ElementInfo elementInfo(logicalId, logicalId, parentLogicalId, Id(), {{"name", name}}, {}, Id(), isEdge);
		logicalModel->addElementToModel(elementInfo);
	}
}
QList<QPair<QString, QString>> PropertyEditorModel::enumValues(const QModelIndex &index) const
{
	if (!index.isValid()) {
		return {};
	}

	const AttributeClassEnum attrClass = mField->child(index.row())->attributeClass();
	// metatype, ids and name are definitely not enums
	if (attrClass != logicalAttribute && attrClass != graphicalAttribute) {
		return {};
	}

	const Id id = attrClass == logicalAttribute
			? mTargetLogicalObject.data(roles::idRole).value<Id>()
			: mTargetGraphicalObject.data(roles::idRole).value<Id>();

	QString propertyName = fullPropertyName(index);

	if (propertyName == "Error") {
		return {};
	}

	/// @todo: null id must not be met here but for some reason sometimes it happens.
	/// This is pretty strange because mTargetLogicalObject without manual modification
	/// becomes invalid index.
	return id.isNull()
			? QList<QPair<QString, QString>>()
			: mEditorManagerInterface.enumValues(id, propertyName);
}
Beispiel #3
0
bool Block::initNextBlocks()
{
    if (id().isNull() || id() == Id::rootId()) {
        error(tr("Control flow break detected, stopping"));
        return false;
    }

    const IdList links = mGraphicalModelApi->graphicalRepoApi().outgoingLinks(id());

    if (links.count() > 1) {
        error(tr("Too many outgoing links"));
        return false;
    }

    if (links.count() == 0) {
        error(tr("No outgoing links, please connect this block to something or use Final Node to end program"));
        return false;
    }

    if (links.count() == 1) {
        const Id nextBlockId = mGraphicalModelApi->graphicalRepoApi().otherEntityFromLink(links[0], id());
        if (nextBlockId.isNull() || nextBlockId == Id::rootId()) {
            error(tr("Outgoing link is not connected"));
            return false;
        }

        mNextBlockId = nextBlockId;
    }

    return true;
}
void QrsMetamodelLoader::parseObjectsOnDiagram(const qrRepo::RepoApi &repo, Metamodel &metamodel, const Id &diagram)
{
	if (diagram.isNull() || diagram == Id::rootId()) {
		return;
	}

	for (const Id &id : repo.children(diagram)) {
		if (!repo.isLogicalElement(id)) {
			continue;
		}

		const Id type = id.type();
		if (type == metamodelEnumType) {
			parseEnum(repo, metamodel, id);
		} else if (type == metamodelPortType) {
			parsePort(repo, metamodel, id);
		} else if (type == metamodelGroupType) {
			parseGroup(repo, metamodel, diagram, id);
		} else if (type == metamodelImportType) {
			parseImport(repo, metamodel, id);
		} else if (type == metamodelNodeType) {
			parseNode(repo, metamodel, diagram, id);
		} else if (type == metamodelEdgeType) {
			parseEdge(repo, metamodel, diagram, id);
		}
	}
}
void QrsMetamodelLoader::parseContainer(const qrRepo::RepoApi &repo, Metamodel &metamodel
		, const Id &id, const QString &diagram)
{
	const Id from = repo.from(id);
	const Id to = repo.to(id);
	if (from.isNull() || to.isNull()) {
		qWarning() << "Containment link" << id << "is not connected!";
		return;
	}

	const QString fromName = validateName(repo, from);
	const QString toName = validateName(repo, to);
	ElementType &fromElement = metamodel.elementType(diagram, fromName);
	ElementType &toElement = metamodel.elementType(diagram, toName);
	metamodel.produceEdge(fromElement, toElement, ElementType::containmentLinkType);
}
void QrsMetamodelLoader::parseExplosion(const qrRepo::RepoApi &repo, Metamodel &metamodel
		, const Id &id, const QString &diagram)
{
	const Id from = repo.from(id);
	const Id to = repo.to(id);
	if (from.isNull() || to.isNull()) {
		qWarning() << "Explosion" << id << "is not connected!";
		return;
	}

	const QString fromName = validateName(repo, from);
	const QString toName = validateName(repo, to);
	ElementType &fromElement = metamodel.elementType(diagram, fromName);
	ElementType &toElement = metamodel.elementType(diagram, toName);
	metamodel.addExplosion(fromElement, toElement, boolProperty(repo, id, "makeReusable")
			, boolProperty(repo, id, "requireImmediateLinkage"));
}
bool PropertyEditorModel::isReference(const QModelIndex &index, const QString &propertyName)
{
	Id id = idByIndex(index);
	if (id.isNull()) {
		return false;
	}

	return mEditorManagerInterface.referenceProperties(id.type()).contains(propertyName);
}
void QrsMetamodelLoader::parseGeneralization(const qrRepo::RepoApi &repo, Metamodel &metamodel, const Id &id
		, const QString &diagram, ElementType *&fromElement, ElementType *&toElement, QString &overridingProperties)
{
	const Id from = repo.from(id);
	const Id to = repo.to(id);
	if (from.isNull() || to.isNull()) {
		qWarning() << "Generalization" << id << "is not connected!";
		return;
	}

	const QString fromName = validateName(repo, from);
	const QString toName = validateName(repo, to);
	fromElement = &metamodel.elementType(diagram, fromName);
	toElement = &metamodel.elementType(diagram, toName);
	metamodel.produceEdge(*toElement, *fromElement, ElementType::generalizationLinkType);

	overridingProperties = stringProperty(repo, id, "overrides");
}
Beispiel #9
0
void Controller::diagramOpened(const Id &diagramId)
{
	if (diagramId.isNull()) {
		return;
	}
	UndoStack *stack = new UndoStack;
	connectStack(stack);
	mDiagramStacks.insert(diagramId.toString(), stack);
	resetAll();
}
QString PropertyEditorModel::typeName(const QModelIndex &index) const
{
	Id id = idByIndex(index);
	if (id.isNull()) {
		return "";
	}

	const QString propertyName = fullPropertyName(index);
	return mEditorManagerInterface.typeName(id, propertyName);
}
Beispiel #11
0
void ExploserView::createRemoveExplosionMenu(const Element * const element, QMenu &contextMenu
		, const Id &outgoingConnection) const
{
	if (outgoingConnection.isNull()) {
		return;
	}

	QAction * const action = contextMenu.addAction(mCustomizer.deleteExplosionMenuName());
	connect(action, SIGNAL(triggered()), SLOT(removeExplosionActionTriggered()));
	action->setData(QVariantList() << element->logicalId().toVariant() << outgoingConnection.toVariant());
}
Beispiel #12
0
QVariant Block::property(const Id &id, const QString &propertyName)
{
	const Id logicalId = mGraphicalModelApi->logicalId(id);
	if (logicalId.isNull()) {
		// If we get here we definitely have such situation:
		// graphical id existed when this Block instance was constructed (or we just will not get here),
		// but now the logical instance has suddenly disppeared.
		error(tr("Block has disappeared!"));
		return QVariant();
	}

	return mLogicalModelApi->propertyByRoleName(logicalId, propertyName);
}
Beispiel #13
0
void Controller::diagramClosed(const Id &diagramId)
{
	if (diagramId.isNull() || !mDiagramStacks.keys().contains(diagramId.toString())) {
		return;
	}

	if (mActiveStack == mDiagramStacks[diagramId.toString()]) {
		mActiveStack = nullptr;
	}

	delete mDiagramStacks[diagramId.toString()];
	mDiagramStacks.remove(diagramId.toString());
	resetAll();
}
SaveConvertionManager::GraphicalFilter SaveConvertionManager::graphicalRecreate(
		const SaveConvertionManager::GraphicalReplacer &replacer
		, const SaveConvertionManager::GraphicalConstructor &constructor)
{
	return [replacer, constructor](const Id &block, GraphicalModelAssistInterface &graphicalApi) {
		// Just iterating throught the elements on some diagram, ignoring the diagram itself.
		if (isDiagramType(block)) {
			return false;
		}

		// For each element trying to find out what to replace it with.
		const Id newType = replacer(block, graphicalApi);
		if (newType.isNull()) {
			// Not every element be replaced, concrete implementation must decide it.
			return false;
		}

		// Then creating new element of some type...
		const Id newBlock = Id::createElementId(newType.editor(), newType.diagram(), newType.element());
		graphicalApi.createElement(graphicalApi.parent(block)
				, newBlock
				, false
				, graphicalApi.name(block)
				, graphicalApi.position(block)
				, graphicalApi.logicalId(block));
		// And initializing it...
		constructor(newBlock, block, graphicalApi);

		const bool isEdge = isEdgeType(block);
		if (isEdge) {
			// If out element is edge then connecting it to same elements as the old one was connected
			graphicalApi.setFrom(newBlock, graphicalApi.from(block));
			graphicalApi.setTo(newBlock, graphicalApi.to(block));
		} else {
			// Replacing old node in all incomming and outgoing edges of the old node with the new one.
			for (const Id &edge : graphicalApi.graphicalRepoApi().outgoingLinks(block)) {
				graphicalApi.mutableGraphicalRepoApi().setProperty(edge, "from", newBlock.toVariant());
			}

			for (const Id &edge : graphicalApi.graphicalRepoApi().incomingLinks(block)) {
				graphicalApi.mutableGraphicalRepoApi().setProperty(edge, "to", newBlock.toVariant());
			}
		}

		// And finally disposing of outdated entity.
		graphicalApi.removeElement(block);
		return true;
	};
}
Beispiel #15
0
QWidget *StartWidget::createProjectsManagementWidget()
{
	mProjectsManagementLayout = new QBoxLayout(QBoxLayout::TopToBottom);
	mProjectsManagementLayout->addStretch();

	mOpenProjectButton = new StyledButton(tr("Open existing project")
			, ":icons/startTab/open.svg");
	connect(mOpenProjectButton, &QPushButton::clicked, this, &StartWidget::openExistingProject);
	mProjectsManagementLayout->addWidget(mOpenProjectButton);

	Id const theOnlyDiagram = mMainWindow->editorManager().theOnlyDiagram();
	if (!theOnlyDiagram.isNull()) {
		Id const editor = mMainWindow->editorManager().editors()[0];
		QString const diagramIdString = mMainWindow->editorManager().diagramNodeNameString(editor, theOnlyDiagram);

		mNewProjectButton = new StyledButton(tr("New project"), ":icons/startTab/open.svg");
		mProjectsManagementLayout->addWidget(mNewProjectButton);

		QSignalMapper *newProjectMapper = new QSignalMapper(this);
		newProjectMapper->setMapping(mNewProjectButton, diagramIdString);
		connect(mNewProjectButton, SIGNAL(clicked()), newProjectMapper, SLOT(map()));
		connect(newProjectMapper, SIGNAL(mapped(QString)), this, SLOT(createProjectWithDiagram(QString)));
	} else {
		if (!mMainWindow->editorManager().editors().isEmpty()) {
			QWidget * const pluginsWidget = createPluginsList();
			mProjectsManagementLayout->addWidget(pluginsWidget, 1);
		} else {
			mOpenProjectButton->hide();
		}
	}

	mOpenInterpreterButton = new StyledButton(tr("Open interpreted diagram")
			, ":icons/startTab/openInterpreted.svg");
	mCreateInterpreterButton = new StyledButton(tr("Create interpreted diagram")
			, ":icons/startTab/createInterpreted.svg");
	connect(mOpenInterpreterButton, SIGNAL(clicked()), this, SLOT(openInterpretedDiagram()));
	connect(mCreateInterpreterButton, SIGNAL(clicked()), this, SLOT(createInterpretedDiagram()));

	mProjectsManagementLayout->addWidget(mCreateInterpreterButton);
	mProjectsManagementLayout->addWidget(mOpenInterpreterButton);
	mProjectsManagementLayout->addStretch();

	QWidget * const result = new QWidget;
	result->setLayout(mProjectsManagementLayout);
	result->setStyleSheet(BrandManager::styles()->startTabProjectsManagementBackgroundStyle());
	return result;
}
Beispiel #16
0
void ExploserView::handleDoubleClick(const Id &id)
{
	Id outgoingLink = mLogicalApi.logicalRepoApi().outgoingExplosion(id);
	if (outgoingLink.isNull()) {
		QList<Explosion> const explosions = mLogicalApi.editorManagerInterface().explosions(id);
		if (!explosions.isEmpty()) {
			const Id diagramType = mLogicalApi.editorManagerInterface()
					.findElementByType(explosions[0].target().element());
			AbstractCommand *createCommand =
					mExploser.createElementWithIncomingExplosionCommand(
							id, diagramType, mModels);
			mController.executeGlobal(createCommand);
			outgoingLink = static_cast<CreateElementsCommand *>(createCommand)->results().first().id();
		}
	}
	goTo(outgoingLink);
}
Beispiel #17
0
void Thread::stepInto(const Id &diagram)
{
	const Id initialNode = findStartingElement(diagram);
	BlockInterface * const block = mBlocksTable.block(initialNode);

	if (initialNode.isNull() || !block) {
		error(tr("No entry point found, please add Initial Node to a diagram"), diagram);
		return;
	}

	if (mStack.count() >= SettingsManager::value("interpreterStackSize").toInt()) {
		error(tr("Stack overflow"));
		return;
	}

	turnOn(block);
}
Beispiel #18
0
void ExploserView::createAddExplosionMenu(const Element * const element
		, QMenu &contextMenu, QList<Explosion> const &explosions
		, const Id &alreadyConnectedElement) const
{
	bool hasAnyActions = false;
	const QString menuName = alreadyConnectedElement.isNull()
			? mCustomizer.addExplosionMenuName()
			: mCustomizer.changeExplosionMenuName();
	QMenu *addExplosionMenu = new QMenu(menuName);

	for (const Explosion &explosion : explosions) {
		for (const Id &elementId : mLogicalApi.logicalRepoApi().logicalElements(explosion.target())) {
			if (alreadyConnectedElement == elementId) {
				continue;
			}
			QAction *action = addExplosionMenu->addAction(mLogicalApi.logicalRepoApi().name(elementId));
			hasAnyActions = true;
			connect(action, SIGNAL(triggered()), SLOT(addExplosionActionTriggered()));
			QList<QVariant> tag;
			tag << element->logicalId().toVariant() << elementId.toVariant();
			action->setData(tag);
		}
	}

	if (hasAnyActions) {
		addExplosionMenu->addSeparator();
	}

	for (const Explosion &explosion : explosions) {
		const Id diagramType = mLogicalApi.editorManagerInterface().findElementByType(explosion.target().element());
		const QString name = mLogicalApi.editorManagerInterface().friendlyName(diagramType);
		const QString editorName = mLogicalApi.editorManagerInterface().friendlyName(Id(diagramType.editor()));
		QAction *action = addExplosionMenu->addAction(tr("New ") + editorName + "/" + name);
		hasAnyActions = true;
		connect(action, SIGNAL(triggered()), SLOT(addExplosionActionTriggered()));
		action->setData(QVariantList() << element->logicalId().toVariant() << diagramType.toVariant());
	}
	contextMenu.addMenu(addExplosionMenu);

	if (alreadyConnectedElement != Id()) {
		QAction * const gotoAction = contextMenu.addAction(mCustomizer.goToConnectedMenuName()
				, this, SLOT(goToActionTriggered()));
		gotoAction->setData(alreadyConnectedElement.toVariant());
	}
}
Beispiel #19
0
void ExploserView::createExpandAction(const Element * const element, QMenu &contextMenu
		, const Id &alreadyConnectedElement) const
{
	if (alreadyConnectedElement.isNull()) {
		return;
	}

	const NodeElement * const node = dynamic_cast<const NodeElement * const>(element);
	if (!node) {
		return;
	}

	QAction *expandAction = contextMenu.addAction(node->isExpanded()
			? mCustomizer.collapseExplosionActionText()
			: mCustomizer.expandExplosionActionText());
	connect(expandAction, SIGNAL(triggered()), SLOT(expandExplosionActionTriggered()));

	expandAction->setData(element->id().toVariant());
}
Beispiel #20
0
Subprograms::GenerationResult Subprograms::generate(ControlFlowGeneratorBase *mainGenerator
		, const QString &indentString)
{
	QMap<Id, QString> declarations;
	QMap<Id, QString> implementations;

	Id toGen = firstToGenerate();
	while (toGen != Id()) {
		mDiscoveredSubprograms[toGen] = true;

		const Id graphicalDiagramId = graphicalId(toGen);
		if (graphicalDiagramId.isNull()) {
			mErrorReporter.addError(QObject::tr("Graphical diagram instance not found"));
			return GenerationResult::fatalError;
		}

		const QString rawIdentifier = mRepo.name(toGen);
		const QString identifier = mNameNormalizer->convert(rawIdentifier);
		if (!checkIdentifier(identifier, rawIdentifier)) {
			return GenerationResult::fatalError;
		}

		ControlFlowGeneratorBase *generator = mainGenerator->cloneFor(graphicalDiagramId, true);
		auto readableGenerator = dynamic_cast<ReadableControlFlowGenerator *>(generator);
		semantics::SemanticTree *controlFlow = generator->generate(Id(), "@@unknown@@");
		if (!controlFlow || (readableGenerator && readableGenerator->cantBeGeneratedIntoStructuredCode())) {
			return GenerationResult::error;
		}

		implementations[toGen] = controlFlow->toString(1, indentString);

		const QString forwardDeclaration = readSubprogramTemplate(toGen, "subprograms/forwardDeclaration.t");
		declarations[toGen] = forwardDeclaration;

		toGen = firstToGenerate();
	}

	obtainCode(declarations, implementations);

	return GenerationResult::success;
}
void QrsMetamodelSaver::saveMetamodel(qrRepo::RepoApi &repo, const Metamodel &metamodel)
{
	const Id metamodelId = metamodelRootDiagramType.sameTypeId();
	repo.addChild(Id::rootId(), metamodelId);
	repo.setName(metamodelId, metamodel.id());
	repo.setProperty(metamodelId, "displayedName", metamodel.friendlyName());
	repo.setProperty(metamodelId, "version", metamodel.version());

	Id lastDiagramId;
	for (const QString &diagram : metamodel.diagrams()) {
		saveDiagram(repo, metamodel, diagram, metamodelId, lastDiagramId);
	}

	if (lastDiagramId.isNull()) {
		// Metamodel contained no diagrams, nowhere to save enums and ports, giving up.
		return;
	}

	for (const QString &enumName : metamodel.enumNames()) {
		saveEnum(repo, metamodel, enumName, lastDiagramId);
	}
}
Beispiel #22
0
bool Subprograms::generate(ControlFlowGeneratorBase *mainGenerator)
{
	QMap<Id, QString> declarations;
	QMap<Id, QString> implementations;

	Id toGen = firstToGenerate();
	while (toGen != Id()) {
		mDiscoveredSubprograms[toGen] = true;

		Id const graphicalDiagramId = graphicalId(toGen);
		if (graphicalDiagramId.isNull()) {
			mErrorReporter.addError(QObject::tr("Graphical diagram instance not found"));
			return false;
		}

		QString const rawIdentifier = mRepo.name(toGen);
		QString const identifier = mNameNormalizer->convert(rawIdentifier);
		if (!checkIdentifier(identifier, rawIdentifier)) {
			return false;
		}

		ControlFlowGeneratorBase *generator = mainGenerator->cloneFor(graphicalDiagramId);
		semantics::SemanticTree *controlFlow = generator->generate();
		if (!controlFlow) {
			return false;
		}

		implementations[toGen] = controlFlow->toString(1);

		QString const forwardDeclaration = readSubprogramTemplate(toGen, "subprograms/forwardDeclaration.t");
		declarations[toGen] = forwardDeclaration;

		toGen = firstToGenerate();
	}

	mergeCode(declarations, implementations);

	return true;
}
Beispiel #23
0
void Repository::addChild(const Id &id, const Id &child, const Id &logicalId)
{
	if (mObjects.contains(id)) {
		if (!mObjects[id]->children().contains(child))
			mObjects[id]->addChild(child);

		if (mObjects.contains(child)) { // should we move element?
			mObjects[child]->setParent(id);
		} else {
			Object * const object = logicalId.isNull()
					? static_cast<Object *>(new LogicalObject(child))
					: static_cast<Object *>(new GraphicalObject(child, id, logicalId))
					;

			object->setParent(id);

			mObjects.insert(child, object);
		}
	} else {
		throw Exception("Repository: Adding child " + child.toString() + " to nonexistent object " + id.toString());
	}
}
Beispiel #24
0
void ExploserView::createAddExplosionMenu(Element const * const element
		, QMenu &contextMenu, QList<Explosion> const &explosions
		, Id const &alreadyConnectedElement) const
{
	bool hasAnyActions = false;
	QString const menuName = alreadyConnectedElement.isNull()
			? mMainWindow->toolManager().customizer()->addExplosionMenuName()
			: mMainWindow->toolManager().customizer()->changeExplosionMenuName();
	QMenu *addExplosionMenu = new QMenu(menuName);

	foreach (Explosion const &explosion, explosions) {
		foreach (Id const &elementId, mLogicalApi->logicalRepoApi().logicalElements(explosion.target())) {
			if (alreadyConnectedElement == elementId) {
				continue;
			}
			QAction *action = addExplosionMenu->addAction(mLogicalApi->logicalRepoApi().name(elementId));
			hasAnyActions = true;
			connect(action, SIGNAL(triggered()), SLOT(addExplosionActionTriggered()));
			QList<QVariant> tag;
			tag << element->logicalId().toVariant() << elementId.toVariant();
			action->setData(tag);
		}
	}
Beispiel #25
0
StartDialog::StartDialog(MainWindow &mainWindow, ProjectManager &projectManager)
		: QDialog(&mainWindow, false)
		, mMainWindow(mainWindow)
		, mProjectManager(projectManager)
{
	setMinimumSize(mMinimumSize);
	setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);

	QTabWidget *tabWidget = new QTabWidget;

	RecentProjectsListWidget *recentProjects = new RecentProjectsListWidget(this);
	tabWidget->addTab(recentProjects, tr("&Recent projects"));

	Id const theOnlyDiagram = mMainWindow.editorManager().theOnlyDiagram();
	if (theOnlyDiagram.isNull()) {
		SuggestToCreateDiagramWidget *diagrams = new SuggestToCreateDiagramWidget(&mMainWindow, this);
		tabWidget->addTab(diagrams, tr("&New project with diagram"));
		connect(diagrams, SIGNAL(userDataSelected(QString)), this, SLOT(createProjectWithDiagram(QString)));
		if (recentProjects->count() == 0) {
			tabWidget->setCurrentWidget(diagrams);
		}
	}

	QCommandLinkButton *openIDLink = new QCommandLinkButton(tr("&Open interpreted diagram"));
	QCommandLinkButton *createIDLink = new QCommandLinkButton(tr("&Create interpreted diagram"));

	QHBoxLayout *openIDLinkLayout = new QHBoxLayout;
	openIDLinkLayout->addWidget(openIDLink);
	mInterpreterButton = openIDLink;

	QHBoxLayout *createIDLinkLayout = new QHBoxLayout;
	createIDLinkLayout->addWidget(createIDLink);
	mCreateInterpreterButton = createIDLink;

	QHBoxLayout *commandLinksLayout = new QHBoxLayout;

	if (theOnlyDiagram != Id()) {
		Id const editor = mMainWindow.editorManager().editors()[0];
		QString const diagramIdString = mMainWindow.editorManager().diagramNodeNameString(editor, theOnlyDiagram);

		QSignalMapper *newProjectMapper = new QSignalMapper(this);
		QCommandLinkButton *newLink = createCommandButton(tr("New project")
				, newProjectMapper, SLOT(map()), QKeySequence::New);
		newProjectMapper->setMapping(newLink, diagramIdString);
		connect(newProjectMapper, SIGNAL(mapped(QString)), this, SLOT(createProjectWithDiagram(QString)));
		commandLinksLayout->addWidget(newLink);
	}

	commandLinksLayout->addWidget(createCommandButton(tr("Open existing project")
			, this, SLOT(openExistingProject()), QKeySequence::Open));

	QVBoxLayout *mainLayout = new QVBoxLayout;
	mainLayout->addWidget(tabWidget);
	mainLayout->addLayout(openIDLinkLayout);
	mainLayout->addLayout(createIDLinkLayout);
	mainLayout->addLayout(commandLinksLayout);

	setLayout(mainLayout);
	setWindowTitle(tr("Start page"));

	connect(openIDLink, SIGNAL(clicked()), this, SLOT(openInterpretedDiagram()));
	connect(createIDLink, SIGNAL(clicked()), this, SLOT(createInterpretedDiagram()));

	connect(recentProjects, SIGNAL(userDataSelected(QString)), this, SLOT(openRecentProject(QString)));
	connect(this, SIGNAL(rejected()), this, SLOT(exitApp()));
}