/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "designmodewidget.h" #include "qmldesignerconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using Core::MiniSplitter; using Core::IEditor; using Core::EditorManager; using namespace QmlDesigner; Q_DECLARE_METATYPE(Core::IEditor*) enum { debug = false }; namespace QmlDesigner { namespace Internal { DocumentWarningWidget::DocumentWarningWidget(DocumentWidget *documentWidget, QWidget *parent) : QFrame(parent), m_errorMessage(new QLabel("Placeholder", this)), m_goToError(new QLabel(this)), m_documentWidget(documentWidget) { setFrameStyle(QFrame::Panel | QFrame::Raised); setLineWidth(1); setForegroundRole(QPalette::ToolTipText); setBackgroundRole(QPalette::ToolTipBase); setAutoFillBackground(true); m_errorMessage->setForegroundRole(QPalette::ToolTipText); m_goToError->setText(tr("Go to error")); m_goToError->setForegroundRole(QPalette::Link); connect(m_goToError, SIGNAL(linkActivated(QString)), this, SLOT(goToError())); QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(20); layout->setSpacing(5); layout->addWidget(m_errorMessage); layout->addWidget(m_goToError, 1, Qt::AlignRight); } void DocumentWarningWidget::setError(const RewriterView::Error &error) { m_error = error; QString str = tr("%3 (%1:%2)").arg(QString::number(error.line()), QString::number(error.column()), error.description()); m_errorMessage->setText(str); resize(layout()->totalSizeHint()); } void DocumentWarningWidget::goToError() { m_documentWidget->textEditor()->gotoLine(m_error.line(), m_error.column()); Core::EditorManager::instance()->ensureEditorManagerVisible(); } DocumentWidget::DocumentWidget(TextEditor::ITextEditor *textEditor, QPlainTextEdit *textEdit, QmlDesigner::DesignDocumentController *document, DesignModeWidget *mainWidget) : QWidget(), m_textEditor(textEditor), m_textBuffer(textEdit), m_document(document), m_mainWidget(mainWidget), m_mainSplitter(0), m_leftSideBar(0), m_rightSideBar(0), m_designToolBar(new QToolBar), m_fakeToolBar(Core::EditorManager::createToolBar(this)), m_isDisabled(false), m_warningWidget(0) { setup(); } DocumentWidget::~DocumentWidget() { // Make sure component widgets are deleted first in SideBarItem::~SideBarItem // before the DesignDocumentController runs (and deletes them again). m_document->deleteLater(); } QmlDesigner::DesignDocumentController *DocumentWidget::document() const { return m_document; } TextEditor::ITextEditor *DocumentWidget::textEditor() const { return m_textEditor; } void DocumentWidget::setAutoSynchronization(bool sync) { if (debug) qDebug() << Q_FUNC_INFO << sync; document()->blockModelSync(!sync); if (sync) { // text editor -> visual editor if (!document()->model()) { document()->loadMaster(m_textBuffer.data()); } QList errors = document()->qmlErrors(); if (errors.isEmpty()) { // set selection to text cursor RewriterView *rewriter = document()->rewriterView(); const int cursorPos = m_textBuffer->textCursor().position(); ModelNode node = nodeForPosition(cursorPos); if (node.isValid()) { rewriter->setSelectedModelNodes(QList() << node); } enable(); } else { disable(errors); } connect(document(), SIGNAL(qmlErrorsChanged(QList)), this, SLOT(updateErrorStatus(QList))); } else { if (document()->model() && document()->qmlErrors().isEmpty()) { RewriterView *rewriter = document()->rewriterView(); // visual editor -> text editor ModelNode selectedNode; if (!rewriter->selectedModelNodes().isEmpty()) selectedNode = rewriter->selectedModelNodes().first(); if (selectedNode.isValid()) { const int nodeOffset = rewriter->nodeOffset(selectedNode); if (nodeOffset > 0) { const ModelNode currentSelectedNode = nodeForPosition(m_textBuffer->textCursor().position()); if (currentSelectedNode != selectedNode) { int line, column; textEditor()->convertPosition(nodeOffset, &line, &column); textEditor()->gotoLine(line, column); } } } } disconnect(document(), SIGNAL(qmlErrorsChanged(QList)), this, SLOT(updateErrorStatus(QList))); } } void DocumentWidget::enable() { if (debug) qDebug() << Q_FUNC_INFO; m_warningWidget->setVisible(false); m_document->documentWidget()->setEnabled(true); m_document->statesEditorWidget()->setEnabled(true); m_leftSideBar->setEnabled(true); m_rightSideBar->setEnabled(true); m_isDisabled = false; } void DocumentWidget::disable(const QList &errors) { if (debug) qDebug() << Q_FUNC_INFO; Q_ASSERT(!errors.isEmpty()); m_warningWidget->setError(errors.first()); m_warningWidget->setVisible(true); m_document->documentWidget()->setEnabled(false); m_document->statesEditorWidget()->setEnabled(false); m_leftSideBar->setEnabled(false); m_rightSideBar->setEnabled(false); m_isDisabled = true; } void DocumentWidget::updateErrorStatus(const QList &errors) { if (debug) qDebug() << Q_FUNC_INFO << errors.count(); if (m_isDisabled && errors.isEmpty()) { enable(); } else if (!errors.isEmpty()) { disable(errors); } } void DocumentWidget::readSettings() { QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("Bauhaus"); m_leftSideBar->readSettings(settings, QLatin1String("LeftSideBar")); m_rightSideBar->readSettings(settings, QLatin1String("RightSideBar")); if (settings->contains("MainSplitter")) { const QByteArray splitterState = settings->value("MainSplitter").toByteArray(); m_mainSplitter->restoreState(splitterState); } settings->endGroup(); } void DocumentWidget::saveSettings() { QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("Bauhaus"); m_leftSideBar->saveSettings(settings, QLatin1String("LeftSideBar")); m_rightSideBar->saveSettings(settings, QLatin1String("RightSideBar")); settings->setValue("MainSplitter", m_mainSplitter->saveState()); settings->endGroup(); } void DocumentWidget::resizeEvent(QResizeEvent *event) { m_warningWidget->move(QPoint(event->size().width() / 2, event->size().height() / 2)); QWidget::resizeEvent(event); } void DocumentWidget::setup() { m_mainSplitter = new MiniSplitter(this); m_mainSplitter->setObjectName("mainSplitter"); // warning frame should be not in layout, but still child of the widget m_warningWidget = new DocumentWarningWidget(this, this); m_warningWidget->setVisible(false); // Left area: Core::SideBarItem *navigatorItem = new Core::SideBarItem(m_document->navigator()); Core::SideBarItem *libraryItem = new Core::SideBarItem(m_document->itemLibrary()); Core::SideBarItem *propertiesItem = new Core::SideBarItem(m_document->allPropertiesBox()); QList leftSideBarItems, rightSideBarItems; leftSideBarItems << navigatorItem << libraryItem; rightSideBarItems << propertiesItem; m_leftSideBar = new Core::SideBar(leftSideBarItems, QList() << navigatorItem << libraryItem); m_rightSideBar = new Core::SideBar(rightSideBarItems, QList() << propertiesItem); m_designToolBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored); m_fakeToolBar->setToolbarCreationFlags(Core::EditorToolBar::FlagsStandalone); m_fakeToolBar->addEditor(textEditor()); m_fakeToolBar->addCenterToolBar(m_designToolBar); m_fakeToolBar->setNavigationVisible(false); // right area: QWidget *centerWidget = new QWidget; { QVBoxLayout *rightLayout = new QVBoxLayout(centerWidget); rightLayout->setMargin(0); rightLayout->setSpacing(0); rightLayout->addWidget(m_fakeToolBar); rightLayout->addWidget(m_document->statesEditorWidget()); rightLayout->addWidget(m_document->documentWidget()); } // m_mainSplitter area: m_mainSplitter->addWidget(m_leftSideBar); m_mainSplitter->addWidget(centerWidget); m_mainSplitter->addWidget(m_rightSideBar); // Finishing touches: m_mainSplitter->setOpaqueResize(false); m_mainSplitter->setStretchFactor(1, 1); m_mainSplitter->setSizes(QList() << 150 << 300 << 150); QLayout *mainLayout = new QBoxLayout(QBoxLayout::RightToLeft, this); mainLayout->setMargin(0); mainLayout->setSpacing(0); mainLayout->addWidget(m_mainSplitter); } bool DocumentWidget::isInNodeDefinition(int nodeOffset, int nodeLength, int cursorPos) const { return (nodeOffset <= cursorPos) && (nodeOffset + nodeLength > cursorPos); } ModelNode DocumentWidget::nodeForPosition(int cursorPos) const { RewriterView *rewriter = m_document->rewriterView(); QList nodes = rewriter->allModelNodes(); ModelNode bestNode; int bestNodeOffset = -1; foreach (const ModelNode &node, nodes) { const int nodeOffset = rewriter->nodeOffset(node); const int nodeLength = rewriter->nodeLength(node); if (isInNodeDefinition(nodeOffset, nodeLength, cursorPos) && (nodeOffset > bestNodeOffset)) { bestNode = node; bestNodeOffset = nodeOffset; } } return bestNode; } // ---------- DesignModeWidget DesignModeWidget::DesignModeWidget(QWidget *parent) : QWidget(parent), m_documentWidgetStack(new QStackedWidget), m_currentDocumentWidget(0), m_currentTextEdit(0), m_syncWithTextEdit(false) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); layout->addWidget(m_documentWidgetStack); m_undoAction = new QAction(tr("&Undo"), this); connect(m_undoAction, SIGNAL(triggered()), this, SLOT(undo())); m_redoAction = new QAction(tr("&Redo"), this); connect(m_redoAction, SIGNAL(triggered()), this, SLOT(redo())); m_deleteAction = new Utils::ParameterAction(tr("Delete"), tr("Delete \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); connect(m_deleteAction, SIGNAL(triggered()), this, SLOT(deleteSelected())); m_cutAction = new Utils::ParameterAction(tr("Cu&t"), tr("Cut \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); connect(m_cutAction, SIGNAL(triggered()), this, SLOT(cutSelected())); m_copyAction = new Utils::ParameterAction(tr("&Copy"), tr("Copy \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); connect(m_copyAction, SIGNAL(triggered()), this, SLOT(copySelected())); m_pasteAction = new Utils::ParameterAction(tr("&Paste"), tr("Paste \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); connect(m_pasteAction, SIGNAL(triggered()), this, SLOT(paste())); m_selectAllAction = new Utils::ParameterAction(tr("Select &All"), tr("Select All \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); connect(m_selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll())); QLabel *defaultBackground = new QLabel(tr("Loading ...")); defaultBackground->setAlignment(Qt::AlignCenter); m_documentWidgetStack->addWidget(defaultBackground); } DesignModeWidget::~DesignModeWidget() { }; void DesignModeWidget::showEditor(Core::IEditor *editor) { QString fileName; QPlainTextEdit *textEdit = 0; TextEditor::ITextEditor *textEditor = 0; if (editor) { fileName = editor->file()->fileName(); textEdit = qobject_cast(editor->widget()); textEditor = qobject_cast(editor); } if (debug) qDebug() << Q_FUNC_INFO << fileName; m_currentTextEdit = textEdit; DocumentWidget *documentWidget = 0; if (textEdit && textEditor && fileName.endsWith(QLatin1String(".qml"))) { if (m_documentHash.contains(textEdit)) { documentWidget = m_documentHash.value(textEdit); } else { DesignDocumentController *newDocument = new DesignDocumentController(0); newDocument->setFileName(fileName); documentWidget = new DocumentWidget(textEditor, textEdit, newDocument, this); connect(documentWidget->document(), SIGNAL(undoAvailable(bool)), this, SLOT(undoAvailable(bool))); connect(documentWidget->document(), SIGNAL(redoAvailable(bool)), this, SLOT(redoAvailable(bool))); // connect(documentWidget->document(), SIGNAL(deleteAvailable(bool)), // this, SLOT(deleteAvailable(bool))); m_documentHash.insert(textEdit, documentWidget); m_documentWidgetStack->addWidget(documentWidget); } } setCurrentDocumentWidget(documentWidget); } void DesignModeWidget::closeEditors(QList editors) { foreach (Core::IEditor* editor, editors) { if (QPlainTextEdit *textEdit = qobject_cast(editor->widget())) { if (m_currentTextEdit == textEdit) { setCurrentDocumentWidget(0); } if (m_documentHash.contains(textEdit)) { if (debug) qDebug() << Q_FUNC_INFO << editor->file()->fileName(); DocumentWidget *document = m_documentHash.take(textEdit); m_documentWidgetStack->removeWidget(document); delete document; } } } } QAction *DesignModeWidget::undoAction() const { return m_undoAction; } QAction *DesignModeWidget::redoAction() const { return m_redoAction; } QAction *DesignModeWidget::deleteAction() const { return m_deleteAction; } QAction *DesignModeWidget::cutAction() const { return m_cutAction; } QAction *DesignModeWidget::copyAction() const { return m_copyAction; } QAction *DesignModeWidget::pasteAction() const { return m_pasteAction; } QAction *DesignModeWidget::selectAllAction() const { return m_selectAllAction; } void DesignModeWidget::undo() { if (m_currentDocumentWidget) m_currentDocumentWidget->document()->undo(); } void DesignModeWidget::redo() { if (m_currentDocumentWidget) m_currentDocumentWidget->document()->redo(); } void DesignModeWidget::deleteSelected() { if (m_currentDocumentWidget) m_currentDocumentWidget->document()->deleteSelected(); } void DesignModeWidget::cutSelected() { if (m_currentDocumentWidget) m_currentDocumentWidget->document()->cutSelected(); } void DesignModeWidget::copySelected() { if (m_currentDocumentWidget) m_currentDocumentWidget->document()->copySelected(); } void DesignModeWidget::paste() { if (m_currentDocumentWidget) m_currentDocumentWidget->document()->paste(); } void DesignModeWidget::selectAll() { if (m_currentDocumentWidget) m_currentDocumentWidget->document()->selectAll(); } void DesignModeWidget::undoAvailable(bool isAvailable) { DesignDocumentController *documentController = qobject_cast(sender()); if (m_currentDocumentWidget && m_currentDocumentWidget->document() == documentController) { m_undoAction->setEnabled(isAvailable); } } void DesignModeWidget::redoAvailable(bool isAvailable) { DesignDocumentController *documentController = qobject_cast(sender()); if (m_currentDocumentWidget && m_currentDocumentWidget->document() == documentController) { m_redoAction->setEnabled(isAvailable); } } void DesignModeWidget::setCurrentDocumentWidget(DocumentWidget *newDocumentWidget) { if (debug) qDebug() << Q_FUNC_INFO << newDocumentWidget; if (m_currentDocumentWidget == newDocumentWidget) return; if (m_currentDocumentWidget) { m_currentDocumentWidget->setAutoSynchronization(false); m_currentDocumentWidget->saveSettings(); } m_currentDocumentWidget = newDocumentWidget; if (m_currentDocumentWidget) { m_currentDocumentWidget->setAutoSynchronization(true); m_documentWidgetStack->setCurrentWidget(m_currentDocumentWidget); m_currentDocumentWidget->readSettings(); m_undoAction->setEnabled(m_currentDocumentWidget->document()->isUndoAvailable()); m_redoAction->setEnabled(m_currentDocumentWidget->document()->isRedoAvailable()); } else { m_documentWidgetStack->setCurrentIndex(0); m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); } } QString DesignModeWidget::contextHelpId() const { if (m_currentDocumentWidget) return m_currentDocumentWidget->document()->contextHelpId(); return QString(); } } // namespace Internal } // namespace Designer