一、HTML编辑器核心类和接口简介
1、HTML编辑器简介
QtCreator最基本的功能是一个文本编辑器。QtCreator还提供了编辑UI文件、QRC文件、PRO/PRI文件以及EXE/DLL/SO文件的功能。
HTML编辑器插件将使QtCreator能够从本地文件系统中加载HTML文件,并且能够查看和编辑。
为了支持新的编辑器类型,需要:
A、实现一个插件类(实现Core::IPlugin接口),暴露出一个“编辑器工厂”。
B、实现“编辑器工厂”,即Core::IEditorFactory接口。Core::IEditorFactory接口提供了帮助创建特定格式编辑器对象的函数。
C、实现编辑器,即Core::IEditor接口。Core::IEditor接口提供了用于辅助编辑一种文件格式(例如 HTML、PDF 等)的函数。编辑器必须提供访问它所要显示或者编辑的文件的函数。
D、实现接口Core::IDocument。Core::IDocument接口用于辅助从磁盘进行数据加载或保存。
2、Core::IDocument接口
Core::IDocument接口将文件操作从用户界面抽象出来,提供用于加载和保存文件以及获取文件名的虚函数。IDocument可以将文件看做具有mime-tyle、“modified”和“read-only”等标记位的对象。IDocument接口在src/plugins/coreplugin/idocument.h中声明:
#ifndef IDOCUMENT_H
#define IDOCUMENT_H
#include "core_global.h"
#include <QObject>
namespace Core {
class MimeType;
class InfoBar;
class CORE_EXPORT IDocument : public QObject
{
Q_OBJECT
public:
// This enum must match the indexes of the reloadBehavior widget
// in generalsettings.ui
enum ReloadSetting {
AlwaysAsk = 0,
ReloadUnmodified = 1,
IgnoreAll = 2
};
enum Utf8BomSetting {
AlwaysAdd = 0,
OnlyKeep = 1,
AlwaysDelete = 2
};
enum ChangeTrigger {
TriggerInternal,
TriggerExternal
};
enum ChangeType {
TypeContents,
TypePermissions,
TypeRemoved
};
enum ReloadBehavior {
BehaviorAsk,
BehaviorSilent
};
enum ReloadFlag {
FlagReload,
FlagIgnore
};
IDocument(QObject *parent = 0);
virtual ~IDocument();
virtual bool save(QString *errorString, const QString &fileName = QString(), bool autoSave = false) = 0;
virtual QString fileName() const = 0;
virtual bool isFileReadOnly() const;
virtual QString defaultPath() const = 0;
virtual QString suggestedFileName() const = 0;
virtual QString mimeType() const = 0;
virtual bool shouldAutoSave() const;
virtual bool isModified() const = 0;
virtual bool isSaveAsAllowed() const = 0;
virtual ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
virtual bool reload(QString *errorString, ReloadFlag flag, ChangeType type) = 0;
virtual void rename(const QString &newName) = 0;
virtual void checkPermissions();
bool autoSave(QString *errorString, const QString &fileName);
void setRestoredFrom(const QString &name);
void removeAutoSaveFile();
bool hasWriteWarning() const { return m_hasWriteWarning; }
void setWriteWarning(bool has) { m_hasWriteWarning = has; }
InfoBar *infoBar();
signals:
void changed();
void aboutToReload();
void reloadFinished(bool success);
void fileNameChanged(const QString &oldName, const QString &newName);
private:
QString m_autoSaveName;
InfoBar *m_infoBar;
bool m_hasWriteWarning;
bool m_restored;
};
} // namespace Core
#endif // IDOCUMENT_H
IDocument与QFile的区别如下:
IDocument关心的是以文件名为参数,将一个文件的内容加载进一个编辑器editor的行为,而QFile仅仅是将文件内容加载到一个QByteArray对象;IDocument需要在用户在编辑器中修改了文件内容的时候发出modified()信号,此时的文件修改并没有写入磁盘;而QFile只有在文件内容写入磁盘时才会发出bytesWritten()信号;IDocument需要处理一个在磁盘被修改的文件如何重新加载到编辑器中,而QFile不需要处理这种问题。
3、Core::IEditor接口
Core::IEditor接口的实现提供了编辑不同类型文件的编辑器。IEditor接口位于src/plugins/coreplugin/editormanager/ieditor.h中。
#ifndef IEDITOR_H
#define IEDITOR_H
#include <coreplugin/core_global.h>
#include <coreplugin/icontext.h>
#include <QMetaType>
namespace Core {
class IDocument;
class CORE_EXPORT IEditor : public IContext
{
Q_OBJECT
public:
IEditor(QObject *parent = 0) : IContext(parent) {}
virtual ~IEditor() {}
virtual bool createNew(const QString &contents = QString()) = 0;
virtual bool open(QString *errorString, const QString &fileName, const QString &realFileName) = 0;
virtual IDocument *document() = 0;
virtual Core::Id id() const = 0;
virtual QString displayName() const = 0;
virtual void setDisplayName(const QString &title) = 0;
virtual bool duplicateSupported() const { return false; }
virtual IEditor *duplicate(QWidget * /*parent*/) { return 0; }
virtual QByteArray saveState() const { return QByteArray(); }
virtual bool restoreState(const QByteArray &/*state*/) { return true; }
virtual int currentLine() const { return 0; }
virtual int currentColumn() const { return 0; }
virtual void gotoLine(int line, int column = 0) { Q_UNUSED(line) Q_UNUSED(column) }
virtual bool isTemporary() const = 0;
virtual QWidget *toolBar() = 0;
virtual bool isDesignModePreferred() const { return false; }
signals:
void changed();
};
} // namespace Core
Q_DECLARE_METATYPE(Core::IEditor*)
#endif // IEDITOR_H
Core::IEditor主要提供以下功能:
A、一个编辑器组件(由Core::IEditor::widget()函数返回)。QtCreator使用这个组件显示编辑文件的内容;
B、Core::IDocument接口实现的document对象的获取(由Core::IEditor::document()函数返回),QtCreator使用document对象触发文件从磁盘加载以及保存到磁盘的操作;
C、一个自定义的工具条 toolbar,QtCreator会在该编辑器可用时自动加载该工具条;
D、在文件中光标的当前位置(Core::IEditor::currentLine()和Core::IEditor::currentColumn()函数)
E、需要显示在“打开文件”列表的名字。
4、Core::IEditorFactory
Core::IEditorFactory提供了用于创建可支持MIME类型的Core::IEditor实例的函数。Core::IEditorFactory接口在src/plugins/coreplugin/editormanager/ieditorfactory.h中声明。
#ifndef IEDITORFACTORY_H
#define IEDITORFACTORY_H
#include <coreplugin/idocumentfactory.h>
namespace Core {
class IEditor;
class CORE_EXPORT IEditorFactory : public Core::IDocumentFactory
{
Q_OBJECT
public:
IEditorFactory(QObject *parent = 0) : IDocumentFactory(parent) {}
virtual IEditor *createEditor(QWidget *parent) = 0;
virtual IDocument *open(const QString &);
};
} // namespace Core
#endif // IEDITORFACTORY_H
IEditorFactory::createEditor()则用于创建一个具体的编辑器实例并将其返回。
5、Core::MimeDatabase
Core::MimeDatabase类用于保存QtCreator所支持的所有的 mime-type;同时也可以辅助判断给定文件的mime-type。
#include <coreplugin/mimedatabase.h>
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
Core::MimeType type1 = mdb->findByFile( QFileInfo("./sample.html") );
qDebug("File Type for sample.html = %s", qPrintable(type1.type()));
Core::MimeType type2 = mdb->findByFile( QFileInfo("./Main.cpp") );
qDebug("File Type for Main.cpp = %s", qPrintable(type2.type()));
Core::MimeType type3 = mdb->findByFile( QFileInfo("./TextEdit.pro") );
qDebug("File Type for TextEdit.pro = %s", qPrintable(type3.type()));
结果如下:
File Type for sample.html = text/plain
File Type for Main.cpp = text/x-c++src
File Type for TextEdit.pro = text/plain
Core::MimeDatabase使用文件名后缀、glob模式(正则表达式)、魔数匹配器计算所给文件的MIME类型。
Core::IEditorFactory接口提供一个指定MIME类型的由Core::IEditor实现的编辑器。以下的过程有助于我们理解QtCreator如何根据给定的文件名选择合适的Core::IEditorFactory。
A、用户点击File->Open,选择要打开的文件
B、QtCreator使用Core::MimeDatabase获取选中文件的MIME类型
C、QtCreator遍历运行所有实现的Core::IEditorFactory,选择支持相应MIME类型的编辑器工厂。
D、QtCreator请求选中的编辑器工厂创建一个编辑器。
E、BaseTextEditor::editorWidget()函数返回组件,在主窗口显示
F、Core::IEditor::open()函数打开选择的文件
6、增加一个MIME类型
如果想要支持一个新的编辑器类型,需要将新编辑器支持的MIME类型注册到Core::MimeDatabase。注册新的MIME类型到Core::MimeDatabase有多种方法。最简单的方法是从一个XML文件注册新的MIME 类型。
假定注册一个MIME类型为text/html,并将其关联*.html文件名,可以创建一个text-html-mimetype.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="text/html">
<sub-class-of type="text/plain"/>
<comment>HTML File</comment>
<glob pattern="*.html"/>
</mime-type>
</mime-info>
注册由XML描述的MIME类型可以使用Core::MimeDatabase::addMimeTypes()函数。
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
QString errMsg;
bool success = mdb->addMimeTypes("text-html-mimetype.xml", errMsg);
注册成功后,QtCreator会映射文件名为*.html的文件到text/plain类型。
二、HTMLEditor编辑器的实现
1、HTML编辑器设计
HTML编辑器作为QtCreator的插件存在,支持HTML文件的查看和编辑。用户可以使用标准的File -> Open菜单打开HTML文件,使用File -> Save菜单保存HTML文件的变化。界面如下:
HTML编辑器在底部有Preview和Source两个按钮,点击Source按钮时,HTML编辑器在QPlainTextEdit组件中显示HTML文件代码。在Source选项页时,用户可以修改HTML文件内容,并且在切换到Preview选项页时能够预览HTML的效果。
HTML编辑器的类设计如下:
HTMLEditorWidget:继承自QTabWidget,提供有两个选项卡的tab组件,用于预览HTML效果和显示HTML代码。
HTMLDocument:实现了Core::IDocument接口,用于在HTMLEditorWidget组件显示的文件。
HTMLEditor:实现了Core::IEditor接口,用于管理HTMLEditorWidget组件
HTMLEditorFactory:实现了Core::IEditorFactory接口,用于创建一个text/html类型的IEditor实例。
HTMLEditorPlugin:实现了Core::IPlugin接口,用于在QtCreator中回调HTMLEditorWidget、HTMLDocument、HTMLEditor、HTMLEditorFactory。