Commit 1c78e200 authored by Friedemann Kleint's avatar Friedemann Kleint

Design mode/Qt Designer: clean-up Part II: Fix undo.

Re-introduce FormEditorFile that delegates dirty handling to the form
window. Change DesignerXmlEditable to be an IEditor that embeds
TextEditable so that the PlainTextEditor can work with it, but delegates
relevant functionality to FormEditorFile.
Centralize all form window creation code that was scattered around
in FormEditorW::createEditor() and have that return a struct Editor
data, which is passed to FormEditorStack.
Update the text editor only on open/createNew/switch away from design
mode.
parent 403d7558
......@@ -39,7 +39,8 @@ HEADERS += formeditorplugin.h \
designerxmleditor.h \
designercontext.h \
faketoolbar.h \
formeditorstack.h
formeditorstack.h \
editordata.h
SOURCES += formeditorplugin.cpp \
formeditorfactory.cpp \
......
......@@ -36,6 +36,7 @@
#include <QtGui/QWidget>
#include <QtCore/QDebug>
#include <QtCore/QSettings>
enum { debug = 0 };
......
......@@ -29,64 +29,157 @@
#include "designerxmleditor.h"
#include "designerconstants.h"
#include "qt_private/formwindowbase_p.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/imode.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/uniqueidmanager.h>
#include <QDebug>
#include <texteditor/basetextdocument.h>
#include <utils/qtcassert.h>
#include <QtDesigner/QDesignerFormWindowInterface>
#include <QtCore/QDebug>
#include <QtCore/QFileInfo>
#include <QtCore/QFile>
namespace Designer {
namespace Internal {
DesignerXmlEditor::DesignerXmlEditor(QWidget *parent) : TextEditor::PlainTextEditor(parent)
DesignerXmlEditorEditable::DesignerXmlEditorEditable(Internal::DesignerXmlEditor *editor,
QDesignerFormWindowInterface *form,
QObject *parent) :
Core::IEditor(parent),
m_textEditable(editor),
m_file(form)
{
setReadOnly(true);
connect(Core::ICore::instance()->editorManager(), SIGNAL(currentEditorChanged(Core::IEditor*)),
SLOT(updateEditorInfoBar(Core::IEditor*)));
Core::UniqueIDManager *uidm = Core::UniqueIDManager::instance();
m_context << uidm->uniqueIdentifier(QLatin1String(Designer::Constants::K_DESIGNER_XML_EDITOR_ID));
m_context << uidm->uniqueIdentifier(QLatin1String(Designer::Constants::C_DESIGNER_XML_EDITOR));
connect(form, SIGNAL(changed()), this, SIGNAL(changed()));
// Revert to saved/load externally modified files
connect(&m_file, SIGNAL(reload(QString)), this, SLOT(slotOpen(QString)));
}
DesignerXmlEditor::~DesignerXmlEditor()
bool DesignerXmlEditorEditable::createNew(const QString &contents)
{
if (Designer::Constants::Internal::debug)
qDebug() << "DesignerXmlEditorEditable::createNew" << contents.size();
syncXmlEditor(QString());
QDesignerFormWindowInterface *form = m_file.formWindow();
QTC_ASSERT(form, return false);
if (contents.isEmpty())
return false;
form->setContents(contents);
if (form->mainContainer() == 0)
return false;
syncXmlEditor(contents);
m_file.setFileName(QString());
return true;
}
bool DesignerXmlEditor::open(const QString &fileName)
void DesignerXmlEditorEditable::slotOpen(const QString &fileName)
{
bool res = TextEditor::PlainTextEditor::open(fileName);
QPlainTextEdit::setReadOnly(true);
return res;
open(fileName);
}
void DesignerXmlEditor::updateEditorInfoBar(Core::IEditor *editor)
bool DesignerXmlEditorEditable::open(const QString &fileName)
{
if (editor == editableInterface()) {
Core::EditorManager::instance()->showEditorInfoBar(Constants::INFO_READ_ONLY,
tr("This file can only be edited in Design Mode."),
"Open Designer", this, SLOT(designerOpened()));
if (Designer::Constants::Internal::debug)
qDebug() << "DesignerXmlEditorEditable::open" << fileName;
QDesignerFormWindowInterface *form = m_file.formWindow();
QTC_ASSERT(form, return false);
if (fileName.isEmpty()) {
setDisplayName(tr("untitled"));
return true;
}
if (!editor)
Core::EditorManager::instance()->hideEditorInfoBar(Constants::INFO_READ_ONLY);
const QFileInfo fi(fileName);
const QString absfileName = fi.absoluteFilePath();
QFile file(absfileName);
if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
return false;
form->setFileName(absfileName);
const QString contents = QString::fromUtf8(file.readAll());
form->setContents(contents);
file.close();
if (!form->mainContainer())
return false;
form->setDirty(false);
syncXmlEditor(contents);
setDisplayName(fi.fileName());
m_file.setFileName(absfileName);
emit changed();
return true;
}
void DesignerXmlEditor::designerOpened()
void DesignerXmlEditorEditable::syncXmlEditor()
{
Core::ICore::instance()->modeManager()->activateMode(Core::Constants::MODE_DESIGN);
if (Designer::Constants::Internal::debug)
qDebug() << "DesignerXmlEditorEditable::syncXmlEditor" << m_file.fileName();
syncXmlEditor(contents());
}
} // namespace Internal
void DesignerXmlEditorEditable::syncXmlEditor(const QString &contents)
{
m_textEditable.editor()->setPlainText(contents);
m_textEditable.editor()->setReadOnly(true);
}
Core::IFile *DesignerXmlEditorEditable::file()
{
return &m_file;
}
QString DesignerXmlEditorEditable::id() const
{
return QLatin1String(Designer::Constants::K_DESIGNER_XML_EDITOR_ID);
}
DesignerXmlEditorEditable::DesignerXmlEditorEditable(Internal::DesignerXmlEditor *editor)
: TextEditor::PlainTextEditorEditable(editor)
QString DesignerXmlEditorEditable::displayName() const
{
Core::UniqueIDManager *uidm = Core::UniqueIDManager::instance();
m_context << uidm->uniqueIdentifier(Designer::Constants::K_DESIGNER_XML_EDITOR_ID);
m_context << uidm->uniqueIdentifier(Designer::Constants::C_DESIGNER_XML_EDITOR);
return m_textEditable.displayName();
}
void DesignerXmlEditorEditable::setDisplayName(const QString &title)
{
m_textEditable.setDisplayName(title);
}
bool DesignerXmlEditorEditable::duplicateSupported() const
{
return false;
}
Core::IEditor *DesignerXmlEditorEditable::duplicate(QWidget *)
{
return 0;
}
QByteArray DesignerXmlEditorEditable::saveState() const
{
return m_textEditable.saveState();
}
bool DesignerXmlEditorEditable::restoreState(const QByteArray &state)
{
return m_textEditable.restoreState(state);
}
QList<int> DesignerXmlEditorEditable::context() const
......@@ -94,17 +187,78 @@ QList<int> DesignerXmlEditorEditable::context() const
return m_context;
}
Core::IEditor *DesignerXmlEditorEditable::duplicate(QWidget *parent)
QWidget *DesignerXmlEditorEditable::widget()
{
return m_textEditable.widget();
}
bool DesignerXmlEditorEditable::isTemporary() const
{
return false;
}
QWidget *DesignerXmlEditorEditable::toolBar()
{
Q_UNUSED(parent);
return 0;
}
QString DesignerXmlEditorEditable::contents() const
{
const qdesigner_internal::FormWindowBase *fw = qobject_cast<const qdesigner_internal::FormWindowBase *>(m_file.formWindow());
QTC_ASSERT(fw, return QString());
return fw->fileContents(); // No warnings about spacers here
}
TextEditor::BaseTextDocument *DesignerXmlEditorEditable::textDocument()
{
return qobject_cast<TextEditor::BaseTextDocument*>(m_textEditable.file());
}
TextEditor::PlainTextEditorEditable *DesignerXmlEditorEditable::textEditable()
{
return &m_textEditable;
}
namespace Internal {
DesignerXmlEditor::DesignerXmlEditor(QDesignerFormWindowInterface *form,
QWidget *parent) :
TextEditor::PlainTextEditor(parent),
m_editable(new DesignerXmlEditorEditable(this, form))
{
setReadOnly(true);
connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)),
SLOT(updateEditorInfoBar(Core::IEditor*)));
}
TextEditor::BaseTextEditorEditable *DesignerXmlEditor::createEditableInterface()
{
return new DesignerXmlEditorEditable(this);
if (Designer::Constants::Internal::debug)
qDebug() << "DesignerXmlEditor::createEditableInterface()";
return m_editable->textEditable();
}
void DesignerXmlEditor::updateEditorInfoBar(Core::IEditor *editor)
{
if (editor == m_editable) {
Core::EditorManager::instance()->showEditorInfoBar(Constants::INFO_READ_ONLY,
tr("This file can only be edited in Design Mode."),
"Open Designer", this, SLOT(designerModeClicked()));
}
if (!editor)
Core::EditorManager::instance()->hideEditorInfoBar(Constants::INFO_READ_ONLY);
}
void DesignerXmlEditor::designerModeClicked()
{
Core::ICore::instance()->modeManager()->activateMode(QLatin1String(Core::Constants::MODE_DESIGN));
}
DesignerXmlEditorEditable *DesignerXmlEditor::designerEditable() const
{
return m_editable;
}
}
} // namespace Designer
......@@ -31,58 +31,106 @@
#define DESIGNERXMLEDITOR_H
#include "designer_export.h"
#include "formwindowfile.h"
#include <texteditor/plaintexteditor.h>
#include <texteditor/basetexteditor.h>
namespace Core {
class IEditor;
class IMode;
}
namespace TextEditor {
class BaseTextDocument;
}
namespace Designer {
namespace Internal {
class DesignerXmlEditor;
}
class DESIGNER_EXPORT DesignerXmlEditorEditable : public TextEditor::PlainTextEditorEditable
// The actual Core::IEditor belonging to Qt Designer. It internally embeds
// a TextEditor::PlainTextEditorEditable which is used to make the
// Read-only edit mode XML text editor work and delegates some functionality
// to it. However, the isDirty() handling is delegated to the FormWindowFile,
// which is the Core::IFile used for it.
class DESIGNER_EXPORT DesignerXmlEditorEditable : public Core::IEditor
{
Q_OBJECT
public:
explicit DesignerXmlEditorEditable(Internal::DesignerXmlEditor *editor);
QList<int> context() const;
explicit DesignerXmlEditorEditable(Internal::DesignerXmlEditor *editor,
QDesignerFormWindowInterface *form,
QObject *parent = 0);
bool duplicateSupported() const { return false; }
Core::IEditor *duplicate(QWidget *parent);
// IEditor
virtual bool createNew(const QString &contents = QString());
virtual bool open(const QString &fileName = QString());
virtual Core::IFile *file();
virtual QString id() const;
virtual QString displayName() const;
virtual void setDisplayName(const QString &title);
virtual bool duplicateSupported() const;
virtual IEditor *duplicate(QWidget *parent);
virtual QByteArray saveState() const;
virtual bool restoreState(const QByteArray &state);
virtual bool isTemporary() const;
virtual QWidget *toolBar();
// IContext
virtual QList<int> context() const;
virtual QWidget *widget();
// For uic code model support
QString contents() const;
TextEditor::BaseTextDocument *textDocument();
TextEditor::PlainTextEditorEditable *textEditable();
public slots:
void syncXmlEditor();
private slots:
void slotOpen(const QString &fileName);
private:
void syncXmlEditor(const QString &contents);
TextEditor::PlainTextEditorEditable m_textEditable;
Internal::FormWindowFile m_file;
QList<int> m_context;
};
/**
* A stub-like, read-only text editor which displays UI files as text. Could be used as a
/* A stub-like, read-only text editor which displays UI files as text. Could be used as a
* read/write editor too, but due to lack of XML editor, highlighting and other such
* functionality, editing is disabled.
*/
* Provides an informational title bar containing a button triggering a
* switch to design mode.
* Internally manages DesignerXmlEditorEditable and uses the plain text
* editable embedded in it. */
namespace Internal {
class DesignerXmlEditor : public TextEditor::PlainTextEditor
{
Q_OBJECT
public:
explicit DesignerXmlEditor(QWidget *parent = 0);
virtual ~DesignerXmlEditor();
bool open(const QString &fileName = QString());
explicit DesignerXmlEditor(QDesignerFormWindowInterface *form,
QWidget *parent = 0);
DesignerXmlEditorEditable *designerEditable() const;
private slots:
void designerOpened();
void designerModeClicked();
void updateEditorInfoBar(Core::IEditor *editor);
protected:
virtual TextEditor::BaseTextEditorEditable *createEditableInterface();
private:
DesignerXmlEditorEditable *m_editable;
};
} // Internal
......
/**************************************************************************
**
** 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.
**
**************************************************************************/
#ifndef EDITORDATA_H
#define EDITORDATA_H
namespace Designer {
class FormWindowEditor;
class DesignerXmlEditorEditable;
namespace Internal {
// Associates XML and its form editor
struct EditorData {
EditorData() : xmlEditor(0), formEditor(0) {}
DesignerXmlEditorEditable *xmlEditor;
Designer::FormWindowEditor *formEditor;
};
} // namespace Internal
} // namespace Designer
#endif // EDITORDATA_H
......@@ -92,9 +92,9 @@ QDockWidget* const* EditorWidget::designerDockWidgets() const
return m_designerDockWidgets;
}
Designer::FormWindowEditor *EditorWidget::createFormWindowEditor(DesignerXmlEditorEditable *xmlEditor)
void EditorWidget::add(const EditorData &d)
{
return m_stack->createFormWindowEditor(xmlEditor);
m_stack->add(d);
}
bool EditorWidget::removeFormWindowEditor(Core::IEditor *xmlEditor)
......
......@@ -34,9 +34,6 @@
#include <utils/fancymainwindow.h>
#include <QtCore/QHash>
#include <QtCore/QVariant>
QT_BEGIN_NAMESPACE
class QDesignerFormWindowInterface;
QT_END_NAMESPACE
......@@ -49,10 +46,11 @@ class FormWindowEditor;
class DesignerXmlEditorEditable;
namespace Internal {
struct EditorData;
class FormEditorStack;
class FormEditorW;
/* Form editor splitter used as editor window. Contains the shared designer
* windows. */
// Design mode main view.
class EditorWidget : public Utils::FancyMainWindow
{
Q_OBJECT
......@@ -63,7 +61,7 @@ public:
QDockWidget* const* designerDockWidgets() const;
// Form editor stack API
Designer::FormWindowEditor *createFormWindowEditor(DesignerXmlEditorEditable *xmlEditor);
void add(const EditorData &d);
bool removeFormWindowEditor(Core::IEditor *xmlEditor);
bool setVisibleEditor(Core::IEditor *xmlEditor);
Designer::FormWindowEditor *formWindowEditorForXmlEditor(const Core::IEditor *xmlEditor) const;
......
......@@ -30,32 +30,34 @@
#include "formeditorfactory.h"
#include "formeditorw.h"
#include "formwindoweditor.h"
#include "editordata.h"
#include "designerconstants.h"
#include "designerxmleditor.h"
#include <coreplugin/icore.h>
#include <coreplugin/fileiconprovider.h>
#include <coreplugin/editormanager/editormanager.h>
#include <texteditor/texteditorsettings.h>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
using namespace Designer::Internal;
using namespace Designer::Constants;
namespace Designer {
namespace Internal {
FormEditorFactory::FormEditorFactory()
: Core::IEditorFactory(Core::ICore::instance()),
m_mimeTypes(QLatin1String(FORM_MIMETYPE))
{
Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance();
iconProvider->registerIconOverlayForSuffix(QIcon(":/formeditor/images/qt_ui.png"),
QLatin1String("ui"));
iconProvider->registerIconOverlayForSuffix(QIcon(QLatin1String(":/formeditor/images/qt_ui.png")),
QLatin1String("ui"));
}
QString FormEditorFactory::id() const
{
return QLatin1String(DESIGNER_XML_EDITOR_ID); //FORMEDITOR_ID);
return QLatin1String(DESIGNER_XML_EDITOR_ID);
}
QString FormEditorFactory::displayName() const
......@@ -71,12 +73,16 @@ Core::IFile *FormEditorFactory::open(const QString &fileName)
Core::IEditor *FormEditorFactory::createEditor(QWidget *parent)
{
DesignerXmlEditor *xmlEditor = new DesignerXmlEditor(parent);
TextEditor::TextEditorSettings::instance()->initializeEditor(xmlEditor);
return xmlEditor->editableInterface();
const EditorData data = FormEditorW::instance()->createEditor(parent);
return data.xmlEditor;
}
QStringList FormEditorFactory::mimeTypes() const
{
return m_mimeTypes;
}
} // namespace Internal
} // namespace Designer
......@@ -45,7 +45,6 @@ namespace Internal {
class FormEditorFactory : public Core::IEditorFactory
{
Q_OBJECT
public:
FormEditorFactory();
......
......@@ -33,28 +33,21 @@
#include "formeditorw.h"
#include "designerconstants.h"
#include <texteditor/basetextdocument.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/modemanager.h>
#include <coreplugin/imode.h>
#include <utils/qtcassert.h>
#include <QDesignerFormWindowInterface>
#include <QDesignerFormWindowManagerInterface>
#include <QDesignerFormEditorInterface>
#include "qt_private/formwindowbase_p.h"
#include <QtCore/QDebug>
namespace Designer {
namespace Internal {
FormEditorStack::FormXmlData::FormXmlData() :
xmlEditor(0), formEditor(0)
{
}
FormEditorStack::FormEditorStack(QWidget *parent) :
QStackedWidget(parent),
m_designerCore(0)
......@@ -62,28 +55,24 @@ FormEditorStack::FormEditorStack(QWidget *parent) :
setObjectName(QLatin1String("FormEditorStack"));
}
Designer::FormWindowEditor *FormEditorStack::createFormWindowEditor(DesignerXmlEditorEditable *xmlEditor)
void FormEditorStack::add(const EditorData &data)
{
FormEditorW *few = FormEditorW::instance();
if (m_designerCore == 0) { // Initialize first time here
m_designerCore = few->designerEditor();
m_designerCore = data.formEditor->formWindow()->core();
connect(m_designerCore->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)),
this, SLOT(updateFormWindowSelectionHandles()));
connect(Core::ModeManager::instance(), SIGNAL(currentModeAboutToChange(Core::IMode*)),
this, SLOT(modeAboutToChange(Core::IMode*)));
}
FormXmlData data;
data.formEditor = few->createFormWindowEditor(this);
data.formEditor->setFile(xmlEditor->file());
data.xmlEditor = xmlEditor;
addWidget(data.formEditor);
m_formEditors.append(data);
setFormEditorData(data.formEditor, xmlEditor->contents());
if (Designer::Constants::Internal::debug)
qDebug() << "FormEditorStack::add" << data.xmlEditor << data.formEditor;
m_formEditors.append(data);
addWidget(data.formEditor);