Core: Merge editor factory classes

Mainly to simplify the implementation and naming.

Kind of follow-up to 862c9694.

There is some consolidation needed as followup as well as some potential
renamings. I'd leave that for separate patches to keep this here
somewhat self-contained.

Change-Id: I152e138a1d5baadec3f7542fed0894b8d4ffc8a3
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
hjk
2023-08-21 11:42:56 +02:00
parent 512da70456
commit 4943f387c7
11 changed files with 278 additions and 352 deletions

View File

@@ -192,7 +192,7 @@ void FilePropertiesDialog::refresh()
const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_filePath);
m_mimeType->setText(mimeType.name());
const EditorTypeList factories = IEditorFactory::preferredEditorTypes(m_filePath);
const EditorFactories factories = IEditorFactory::preferredEditorTypes(m_filePath);
m_defaultEditor->setText(!factories.isEmpty() ? factories.at(0)->displayName()
: Tr::tr("Undefined"));

View File

@@ -797,12 +797,10 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file
realFp = filePath;
}
EditorTypeList factories = EditorType::preferredEditorTypes(filePath);
if (!(flags & EditorManager::AllowExternalEditor)) {
factories = Utils::filtered(factories, [](EditorType *type) {
return type->asEditorFactory() != nullptr;
});
}
EditorFactories factories = IEditorFactory::preferredEditorTypes(filePath);
if (!(flags & EditorManager::AllowExternalEditor))
factories = Utils::filtered(factories, &IEditorFactory::isInternalEditor);
if (factories.isEmpty()) {
Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath);
QMessageBox msgbox(QMessageBox::Critical, ::Core::Tr::tr("File Error"),
@@ -813,9 +811,9 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file
return nullptr;
}
if (editorId.isValid()) {
EditorType *factory = EditorType::editorTypeForId(editorId);
IEditorFactory *factory = IEditorFactory::editorFactoryForId(editorId);
if (factory) {
QTC_CHECK(factory->asEditorFactory() || (flags & EditorManager::AllowExternalEditor));
QTC_CHECK(factory->isInternalEditor() || (flags & EditorManager::AllowExternalEditor));
factories.removeOne(factory);
factories.push_front(factory);
}
@@ -824,12 +822,12 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file
IEditor *editor = nullptr;
auto overrideCursor = Utils::OverrideCursor(QCursor(Qt::WaitCursor));
EditorType *factory = factories.takeFirst();
IEditorFactory *factory = factories.takeFirst();
while (factory) {
QString errorString;
if (factory->asEditorFactory()) {
editor = createEditor(factory->asEditorFactory(), filePath);
if (factory->isInternalEditor()) {
editor = createEditor(factory, filePath);
if (!editor) {
factory = factories.isEmpty() ? nullptr : factories.takeFirst();
continue;
@@ -857,10 +855,10 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file
// can happen e.g. when trying to open an completely empty .qrc file
QTC_CHECK(openResult == IDocument::OpenResult::CannotHandle);
} else {
QTC_ASSERT(factory->asExternalEditor(),
QTC_ASSERT(factory->isExternalEditor(),
factory = factories.isEmpty() ? nullptr : factories.takeFirst();
continue);
if (factory->asExternalEditor()->startEditor(filePath, &errorString))
if (factory->startEditor(filePath, &errorString))
break;
}
@@ -873,12 +871,12 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file
QMessageBox::Open | QMessageBox::Cancel,
ICore::dialogParent());
EditorType *selectedFactory = nullptr;
IEditorFactory *selectedFactory = nullptr;
if (!factories.isEmpty()) {
auto button = qobject_cast<QPushButton *>(msgbox.button(QMessageBox::Open));
QTC_ASSERT(button, return nullptr);
auto menu = new QMenu(button);
for (EditorType *factory : std::as_const(factories)) {
for (IEditorFactory *factory : std::as_const(factories)) {
QAction *action = menu->addAction(factory->displayName());
connect(action, &QAction::triggered, &msgbox, [&selectedFactory, factory, &msgbox] {
selectedFactory = factory;
@@ -1148,7 +1146,7 @@ Id EditorManagerPrivate::getOpenWithEditorId(const Utils::FilePath &fileName, bo
QList<Id> allEditorIds;
QStringList allEditorDisplayNames;
// Built-in
const EditorTypeList editors = EditorType::preferredEditorTypes(fileName);
const EditorFactories editors = IEditorFactory::preferredEditorTypes(fileName);
const int size = editors.size();
allEditorDisplayNames.reserve(size);
for (int i = 0; i < size; i++) {
@@ -1166,13 +1164,13 @@ Id EditorManagerPrivate::getOpenWithEditorId(const Utils::FilePath &fileName, bo
return Id();
const Id selectedId = allEditorIds.at(dialog.editor());
if (isExternalEditor) {
EditorType *type = EditorType::editorTypeForId(selectedId);
*isExternalEditor = type && type->asExternalEditor() != nullptr;
IEditorFactory *type = IEditorFactory::editorFactoryForId(selectedId);
*isExternalEditor = type && type->isExternalEditor();
}
return selectedId;
}
static QMap<QString, QVariant> toMap(const QHash<Utils::MimeType, EditorType *> &hash)
static QMap<QString, QVariant> toMap(const QHash<Utils::MimeType, IEditorFactory *> &hash)
{
QMap<QString, QVariant> map;
auto it = hash.begin();
@@ -1184,18 +1182,18 @@ static QMap<QString, QVariant> toMap(const QHash<Utils::MimeType, EditorType *>
return map;
}
static QHash<Utils::MimeType, EditorType *> fromMap(const QMap<QString, QVariant> &map)
static QHash<Utils::MimeType, IEditorFactory *> fromMap(const QMap<QString, QVariant> &map)
{
const EditorTypeList factories = EditorType::allEditorTypes();
QHash<Utils::MimeType, EditorType *> hash;
const EditorFactories factories = IEditorFactory::allEditorFactories();
QHash<Utils::MimeType, IEditorFactory *> hash;
auto it = map.begin();
const auto end = map.end();
while (it != end) {
const Utils::MimeType mimeType = Utils::mimeTypeForName(it.key());
if (mimeType.isValid()) {
const Id factoryId = Id::fromSetting(it.value());
EditorType *factory = Utils::findOrDefault(factories,
Utils::equal(&EditorType::id, factoryId));
IEditorFactory *factory = Utils::findOrDefault(factories,
Utils::equal(&IEditorFactory::id, factoryId));
if (factory)
hash.insert(mimeType, factory);
}
@@ -1225,7 +1223,7 @@ void EditorManagerPrivate::readSettings()
else
HostOsInfo::setOverrideFileNameCaseSensitivity(sensitivity);
const QHash<Utils::MimeType, EditorType *> preferredEditorFactories = fromMap(
const QHash<Utils::MimeType, IEditorFactory *> preferredEditorFactories = fromMap(
qs->value(preferredEditorFactoriesKey).toMap());
setUserPreferredEditorTypes(preferredEditorFactories);
@@ -1266,12 +1264,12 @@ void EditorManagerPrivate::writeFileSystemSensitivity(Utils::QtcSettings *settin
HostOsInfo::hostOs())));
}
EditorFactoryList EditorManagerPrivate::findFactories(Id editorId, const FilePath &filePath)
EditorFactories EditorManagerPrivate::findFactories(Id editorId, const FilePath &filePath)
{
if (debugEditorManager)
qDebug() << Q_FUNC_INFO << editorId.name() << filePath;
EditorFactoryList factories;
EditorFactories factories;
if (!editorId.isValid()) {
factories = IEditorFactory::preferredEditorFactories(filePath);
} else {
@@ -2810,11 +2808,11 @@ void EditorManager::populateOpenWithMenu(QMenu *menu, const FilePath &filePath)
{
menu->clear();
const EditorTypeList factories = IEditorFactory::preferredEditorTypes(filePath);
const EditorFactories factories = IEditorFactory::preferredEditorTypes(filePath);
const bool anyMatches = !factories.empty();
if (anyMatches) {
// Add all suitable editors
for (EditorType *editorType : factories) {
for (IEditorFactory *editorType : factories) {
const Id editorId = editorType->id();
// Add action to open with this very editor factory
QString const actionTitle = editorType->displayName();
@@ -2824,8 +2822,8 @@ void EditorManager::populateOpenWithMenu(QMenu *menu, const FilePath &filePath)
// crashes happen, because the editor instance is deleted by openEditorWith
// while the menu is still being processed.
connect(action, &QAction::triggered, d, [filePath, editorId] {
EditorType *type = EditorType::editorTypeForId(editorId);
if (type && type->asExternalEditor())
IEditorFactory *type = IEditorFactory::editorFactoryForId(editorId);
if (type && type->isExternalEditor())
EditorManager::openExternalEditor(filePath, editorId);
else
EditorManagerPrivate::openEditorWith(filePath, editorId);
@@ -2839,11 +2837,10 @@ void EditorManager::runWithTemporaryEditor(const Utils::FilePath &filePath,
const std::function<void (IEditor *)> &callback)
{
const MimeType mt = mimeTypeForFile(filePath, MimeMatchMode::MatchDefaultAndRemote);
const QList<IEditorFactory *> factories = Utils::transform(
EditorType::defaultEditorTypes(mt), [](EditorType *t) {
return t->asEditorFactory(); });
const QList<IEditorFactory *> factories = IEditorFactory::defaultEditorFactories(mt);
for (IEditorFactory * const factory : factories) {
if (!factory)
QTC_ASSERT(factory, continue);
if (!factory->isInternalEditor())
continue;
std::unique_ptr<IEditor> editor(factory->createEditor());
if (!editor)
@@ -3103,8 +3100,11 @@ bool EditorManager::autoSaveAfterRefactoring()
*/
bool EditorManager::openExternalEditor(const FilePath &filePath, Id editorId)
{
IExternalEditor *ee = Utils::findOrDefault(IExternalEditor::allExternalEditors(),
Utils::equal(&IExternalEditor::id, editorId));
IEditorFactory *ee = Utils::findOrDefault(IEditorFactory::allEditorFactories(),
[editorId](IEditorFactory *factory) {
return factory->isExternalEditor() && factory->id() == editorId;
});
if (!ee)
return false;
QString errorMessage;
@@ -3228,7 +3228,7 @@ IEditor *EditorManager::openEditorWithContents(Id editorId,
}
const FilePath filePath = FilePath::fromString(title);
EditorFactoryList factories = EditorManagerPrivate::findFactories(editorId, filePath);
EditorFactories factories = EditorManagerPrivate::findFactories(editorId, filePath);
if (factories.isEmpty())
return nullptr;

View File

@@ -167,7 +167,7 @@ private:
static OpenEditorsWindow *windowPopup();
static void showPopupOrSelectDocument();
static EditorFactoryList findFactories(Utils::Id editorId, const Utils::FilePath &filePath);
static EditorFactories findFactories(Utils::Id editorId, const Utils::FilePath &filePath);
static IEditor *createEditor(IEditorFactory *factory, const Utils::FilePath &filePath);
static void addEditor(IEditor *editor);
static void removeEditor(IEditor *editor, bool removeSusependedEntry);

View File

@@ -51,29 +51,26 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
IEditorFactory is then asked to create an editor.
Implementations should set the properties of the IEditorFactory subclass in
their constructor with EditorType::setId(), EditorType::setDisplayName(),
EditorType::setMimeTypes(), and setEditorCreator()
their constructor with IEditorFactory::setId(), IEditorFactory::setDisplayName(),
IEditorFactory::setMimeTypes(), and setEditorCreator()
IEditorFactory instances automatically register themselves in \QC in their
constructor.
\sa Core::EditorType
There are two varieties of editors: Internal and external. Internal editors
open within the main editing area of Qt Creator. An IEditorFactory defines
an internal editor by using the \c setEditorCreator function. External
editors are external applications and are defined by using the
\c setEditorStarter function. They are accessible by the user using
the \uicontrol{Open With} dialog
\sa Core::IEditor
\sa Core::IDocument
\sa Core::EditorManager
*/
/*!
\class Core::EditorType
\inheaderfile coreplugin/editormanager/ieditorfactory.h
\inmodule QtCreator
\brief The EditorType class is the base class for Core::IEditorFactory and
Core::IExternalEditor.
*/
/*!
\fn void Core::EditorType::addMimeType(const QString &mimeType)
\fn void Core::IEditorFactory::addMimeType(const QString &mimeType)
Adds \a mimeType to the list of MIME types supported by this editor type.
@@ -82,7 +79,7 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
*/
/*!
\fn QString Core::EditorType::displayName() const
\fn QString Core::IEditorFactory::displayName() const
Returns a user-visible description of the editor type.
@@ -90,7 +87,7 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
*/
/*!
\fn Utils::Id Core::EditorType::id() const
\fn Utils::Id Core::IEditorFactory::id() const
Returns the ID of the editors' document type.
@@ -98,7 +95,7 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
*/
/*!
\fn QString Core::EditorType::mimeTypes() const
\fn QString Core::IEditorFactory::mimeTypes() const
Returns the list of supported MIME types of this editor type.
@@ -107,7 +104,7 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
*/
/*!
\fn void Core::EditorType::setDisplayName(const QString &displayName)
\fn void Core::IEditorFactory::setDisplayName(const QString &displayName)
Sets the \a displayName of the editor type. This is for example shown in
the \uicontrol {Open With} menu and the MIME type preferences.
@@ -116,7 +113,7 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
*/
/*!
\fn void Core::EditorType::setId(Utils::Id id)
\fn void Core::IEditorFactory::setId(Utils::Id id)
Sets the \a id of the editors' document type. This must be the same as the
IDocument::id() of the documents returned by created editors.
@@ -125,7 +122,7 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
*/
/*!
\fn void Core::EditorType::setMimeTypes(const QStringList &mimeTypes)
\fn void Core::IEditorFactory::setMimeTypes(const QStringList &mimeTypes)
Sets the MIME types supported by the editor type to \a mimeTypes.
@@ -133,88 +130,8 @@ static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType,
\sa mimeTypes()
*/
static QList<EditorType *> g_editorTypes;
static QHash<Utils::MimeType, EditorType *> g_userPreferredEditorTypes;
static QList<IEditorFactory *> g_editorFactories;
/*!
\internal
*/
EditorType::EditorType()
{
g_editorTypes.append(this);
}
/*!
\internal
*/
EditorType::~EditorType()
{
g_editorTypes.removeOne(this);
}
/*!
Returns all registered internal and external editors.
*/
const EditorTypeList EditorType::allEditorTypes()
{
return g_editorTypes;
}
EditorType *EditorType::editorTypeForId(const Utils::Id &id)
{
return Utils::findOrDefault(allEditorTypes(), Utils::equal(&EditorType::id, id));
}
/*!
Returns all available internal and external editors for the \a mimeType in the
default order: Editor types ordered by MIME type hierarchy, internal editors
first.
*/
const EditorTypeList EditorType::defaultEditorTypes(const MimeType &mimeType)
{
EditorTypeList result;
const EditorTypeList allTypes = EditorType::allEditorTypes();
const EditorTypeList allEditorFactories = Utils::filtered(allTypes, [](EditorType *e) {
return e->asEditorFactory() != nullptr;
});
const EditorTypeList allExternalEditors = Utils::filtered(allTypes, [](EditorType *e) {
return e->asExternalEditor() != nullptr;
});
mimeTypeFactoryLookup(mimeType, allEditorFactories, &result);
mimeTypeFactoryLookup(mimeType, allExternalEditors, &result);
return result;
}
const EditorTypeList EditorType::preferredEditorTypes(const FilePath &filePath)
{
// default factories by mime type
const Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath,
MimeMatchMode::MatchDefaultAndRemote);
EditorTypeList factories = defaultEditorTypes(mimeType);
// user preferred factory to front
EditorType *userPreferred = Internal::userPreferredEditorTypes().value(mimeType);
if (userPreferred) {
factories.removeAll(userPreferred);
factories.prepend(userPreferred);
}
// make binary editor first internal editor for text files > 48 MB
if (filePath.fileSize() > EditorManager::maxTextFileSize() && mimeType.inherits("text/plain")) {
const Utils::MimeType binary = Utils::mimeTypeForName("application/octet-stream");
const EditorTypeList binaryEditors = defaultEditorTypes(binary);
if (!binaryEditors.isEmpty()) {
EditorType *binaryEditor = binaryEditors.first();
factories.removeAll(binaryEditor);
int insertionIndex = 0;
while (factories.size() > insertionIndex
&& factories.at(insertionIndex)->asExternalEditor() != nullptr) {
++insertionIndex;
}
factories.insert(insertionIndex, binaryEditor);
}
}
return factories;
}
static QHash<Utils::MimeType, IEditorFactory *> g_userPreferredEditorTypes;
/*!
Creates an IEditorFactory.
@@ -235,54 +152,124 @@ IEditorFactory::~IEditorFactory()
}
/*!
\internal
Returns all registered internal and external editors.
*/
const EditorFactoryList IEditorFactory::allEditorFactories()
const EditorFactories IEditorFactory::allEditorFactories()
{
return g_editorFactories;
}
IEditorFactory *IEditorFactory::editorFactoryForId(const Utils::Id &id)
{
return Utils::findOrDefault(allEditorFactories(), Utils::equal(&IEditorFactory::id, id));
}
/*!
Returns all available internal and external editors for the \a mimeType in the
default order: Editor types ordered by MIME type hierarchy, internal editors
first.
*/
const EditorFactories IEditorFactory::defaultEditorFactories(const MimeType &mimeType)
{
EditorFactories result;
const EditorFactories allTypes = IEditorFactory::allEditorFactories();
const EditorFactories allEditorFactories
= Utils::filtered(allTypes, &IEditorFactory::isInternalEditor);
const EditorFactories allExternalEditors
= Utils::filtered(allTypes, &IEditorFactory::isExternalEditor);
mimeTypeFactoryLookup(mimeType, allEditorFactories, &result);
mimeTypeFactoryLookup(mimeType, allExternalEditors, &result);
return result;
}
// FIXME: Consolidate with preferredEditorFactories()
const EditorFactories IEditorFactory::preferredEditorTypes(const FilePath &filePath)
{
// default factories by mime type
const Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath,
MimeMatchMode::MatchDefaultAndRemote);
EditorFactories factories = defaultEditorFactories(mimeType);
// user preferred factory to front
IEditorFactory *userPreferred = Internal::userPreferredEditorTypes().value(mimeType);
if (userPreferred) {
factories.removeAll(userPreferred);
factories.prepend(userPreferred);
}
// make binary editor first internal editor for text files > 48 MB
if (filePath.fileSize() > EditorManager::maxTextFileSize() && mimeType.inherits("text/plain")) {
const Utils::MimeType binary = Utils::mimeTypeForName("application/octet-stream");
const EditorFactories binaryEditors = defaultEditorFactories(binary);
if (!binaryEditors.isEmpty()) {
IEditorFactory *binaryEditor = binaryEditors.first();
factories.removeAll(binaryEditor);
int insertionIndex = 0;
while (factories.size() > insertionIndex
&& !factories.at(insertionIndex)->isInternalEditor()) {
++insertionIndex;
}
factories.insert(insertionIndex, binaryEditor);
}
}
return factories;
}
/*!
Returns the available editor factories for \a filePath in order of
preference. That is the default order for the document's MIME type but with
a user overridden default editor first, and the binary editor as the very
first item if a text document is too large to be opened as a text file.
*/
const EditorFactoryList IEditorFactory::preferredEditorFactories(const FilePath &filePath)
const EditorFactories IEditorFactory::preferredEditorFactories(const FilePath &filePath)
{
const auto defaultEditorFactories = [](const MimeType &mimeType) {
QList<IEditorFactory *> editorFactories;
for (EditorType *type : defaultEditorTypes(mimeType)) {
if (IEditorFactory *editorFactory = type->asEditorFactory())
editorFactories.append(editorFactory);
for (IEditorFactory *type : IEditorFactory::defaultEditorFactories(mimeType)) {
if (type->isInternalEditor())
editorFactories.append(type);
}
return editorFactories;
};
// default factories by mime type
const Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath);
EditorFactoryList factories = defaultEditorFactories(mimeType);
EditorFactories factories = defaultEditorFactories(mimeType);
const auto factories_moveToFront = [&factories](IEditorFactory *f) {
factories.removeAll(f);
factories.prepend(f);
};
// user preferred factory to front
EditorType *userPreferred = Internal::userPreferredEditorTypes().value(mimeType);
if (userPreferred && userPreferred->asEditorFactory())
factories_moveToFront(userPreferred->asEditorFactory());
IEditorFactory *userPreferred = Internal::userPreferredEditorTypes().value(mimeType);
if (userPreferred && userPreferred->isInternalEditor())
factories_moveToFront(userPreferred);
// open text files > 48 MB in binary editor
if (filePath.fileSize() > EditorManager::maxTextFileSize()
&& mimeType.inherits("text/plain")) {
const Utils::MimeType binary = Utils::mimeTypeForName("application/octet-stream");
const EditorFactoryList binaryEditors = defaultEditorFactories(binary);
const EditorFactories binaryEditors = defaultEditorFactories(binary);
if (!binaryEditors.isEmpty())
factories_moveToFront(binaryEditors.first());
}
return factories;
}
/**
Returns true if this factory creates internal editors.
*/
bool IEditorFactory::isInternalEditor() const
{
return bool(m_creator);
}
/**
Returns true if this factory creates external editors.
*/
bool IEditorFactory::isExternalEditor() const
{
return bool(m_starter);
}
/*!
Creates an editor.
Creates an internal editor.
Uses the function set with setEditorCreator() to create the editor.
@@ -294,21 +281,54 @@ IEditor *IEditorFactory::createEditor() const
return m_creator();
}
/*!
Starts an external editor.
Uses the function set with setEditorStarter() to start the editor.
\sa setEditorStarter()
*/
bool IEditorFactory::startEditor(const FilePath &filePath, QString *errorMessage)
{
QTC_ASSERT(m_starter, return false);
return m_starter(filePath, errorMessage);
}
/*!
Sets the function that is used to create an editor instance in
createEditor() to \a creator.
This is mutually exclusive with the use of setEditorStarter.
\sa createEditor()
*/
void IEditorFactory::setEditorCreator(const std::function<IEditor *()> &creator)
{
QTC_CHECK(!m_starter);
// The check below triggers within the TextEditorFactory sub-hierarchy
// as the base TextEditorFactory already sets as simple creator.
//QTC_CHECK(!m_creator);
m_creator = creator;
}
/*!
Opens the editor with \a fileName. Returns \c true on success or \c false
on failure along with the error in \a errorMessage.
This is mutually exclusive with the use of setEditorCreator.
*/
void IEditorFactory::setEditorStarter(const std::function<bool(const FilePath &, QString *)> &starter)
{
QTC_CHECK(!m_starter);
QTC_CHECK(!m_creator);
m_starter = starter;
}
/*!
\internal
*/
QHash<Utils::MimeType, Core::EditorType *> Core::Internal::userPreferredEditorTypes()
QHash<Utils::MimeType, IEditorFactory *> Internal::userPreferredEditorTypes()
{
return g_userPreferredEditorTypes;
}
@@ -316,64 +336,10 @@ QHash<Utils::MimeType, Core::EditorType *> Core::Internal::userPreferredEditorTy
/*!
\internal
*/
void Internal::setUserPreferredEditorTypes(const QHash<Utils::MimeType, EditorType *> &factories)
void Internal::setUserPreferredEditorTypes(const QHash<Utils::MimeType, IEditorFactory *> &factories)
{
g_userPreferredEditorTypes = factories;
}
/*!
\class Core::IExternalEditor
\inheaderfile coreplugin/editormanager/ieditorfactory.h
\inmodule QtCreator
\ingroup mainclasses
\brief The IExternalEditor class enables registering an external
editor in the \uicontrol{Open With} dialog.
*/
/*!
\fn bool Core::IExternalEditor::startEditor(const Utils::FilePath &fileName, QString *errorMessage)
Opens the editor with \a fileName. Returns \c true on success or \c false
on failure along with the error in \a errorMessage.
*/
static QList<IExternalEditor *> g_externalEditors;
/*!
\internal
*/
IExternalEditor::IExternalEditor()
{
g_externalEditors.append(this);
}
/*!
\internal
*/
IExternalEditor::~IExternalEditor()
{
g_externalEditors.removeOne(this);
}
/*!
Returns all available external editors.
*/
const ExternalEditorList IExternalEditor::allExternalEditors()
{
return g_externalEditors;
}
/*!
Returns all external editors available for this \a mimeType in the default
order (editors ordered by MIME type hierarchy).
*/
const ExternalEditorList IExternalEditor::externalEditors(const Utils::MimeType &mimeType)
{
ExternalEditorList rc;
const ExternalEditorList allEditors = IExternalEditor::allExternalEditors();
mimeTypeFactoryLookup(mimeType, allEditors, &rc);
return rc;
}
} // Core

View File

@@ -18,77 +18,48 @@ class MimeType;
namespace Core {
class IExternalEditor;
class IEditor;
class IEditorFactory;
class EditorType;
using EditorFactoryList = QList<IEditorFactory *>;
using EditorTypeList = QList<EditorType *>;
using ExternalEditorList = QList<IExternalEditor *>;
using EditorFactories = QList<IEditorFactory *>;
class CORE_EXPORT EditorType
class CORE_EXPORT IEditorFactory
{
public:
virtual ~EditorType();
virtual ~IEditorFactory();
static const EditorTypeList allEditorTypes();
static EditorType *editorTypeForId(const Utils::Id &id);
static const EditorTypeList defaultEditorTypes(const Utils::MimeType &mimeType);
static const EditorTypeList preferredEditorTypes(const Utils::FilePath &filePath);
static const EditorFactories allEditorFactories();
static IEditorFactory *editorFactoryForId(const Utils::Id &id);
static const EditorFactories defaultEditorFactories(const Utils::MimeType &mimeType);
static const EditorFactories preferredEditorTypes(const Utils::FilePath &filePath);
static const EditorFactories preferredEditorFactories(const Utils::FilePath &filePath);
Utils::Id id() const { return m_id; }
QString displayName() const { return m_displayName; }
QStringList mimeTypes() const { return m_mimeTypes; }
virtual IEditorFactory *asEditorFactory() { return nullptr; };
virtual IExternalEditor *asExternalEditor() { return nullptr; };
bool isInternalEditor() const;
bool isExternalEditor() const;
IEditor *createEditor() const;
bool startEditor(const Utils::FilePath &filePath, QString *errorMessage);
protected:
EditorType();
IEditorFactory();
void setId(Utils::Id id) { m_id = id; }
void setDisplayName(const QString &displayName) { m_displayName = displayName; }
void setMimeTypes(const QStringList &mimeTypes) { m_mimeTypes = mimeTypes; }
void addMimeType(const QString &mimeType) { m_mimeTypes.append(mimeType); }
void setEditorCreator(const std::function<IEditor *()> &creator);
void setEditorStarter(const std::function<bool(const Utils::FilePath &, QString *)> &starter);
private:
Utils::Id m_id;
QString m_displayName;
QStringList m_mimeTypes;
};
class CORE_EXPORT IEditorFactory : public EditorType
{
public:
IEditorFactory();
~IEditorFactory() override;
static const EditorFactoryList allEditorFactories();
static const EditorFactoryList preferredEditorFactories(const Utils::FilePath &filePath);
IEditor *createEditor() const;
IEditorFactory *asEditorFactory() override { return this; }
protected:
void setEditorCreator(const std::function<IEditor *()> &creator);
private:
std::function<IEditor *()> m_creator;
};
class CORE_EXPORT IExternalEditor : public EditorType
{
public:
explicit IExternalEditor();
~IExternalEditor() override;
static const ExternalEditorList allExternalEditors();
static const ExternalEditorList externalEditors(const Utils::MimeType &mimeType);
IExternalEditor *asExternalEditor() override { return this; }
virtual bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) = 0;
std::function<bool(const Utils::FilePath &, QString *)> m_starter;
};
} // namespace Core

View File

@@ -9,12 +9,12 @@
namespace Core {
class EditorType;
class IEditorFactory;
namespace Internal {
QHash<Utils::MimeType, EditorType *> userPreferredEditorTypes();
void setUserPreferredEditorTypes(const QHash<Utils::MimeType, EditorType *> &factories);
QHash<Utils::MimeType, IEditorFactory *> userPreferredEditorTypes();
void setUserPreferredEditorTypes(const QHash<Utils::MimeType, IEditorFactory *> &factories);
} // Internal
} // Core

View File

@@ -7,7 +7,6 @@
#include <utils/filepath.h>
#include <QStringList>
#include <QUrl>
#include <QDesktopServices>
@@ -20,20 +19,19 @@ SystemEditor::SystemEditor()
setId("CorePlugin.OpenWithSystemEditor");
setDisplayName(Tr::tr("System Editor"));
setMimeTypes({"application/octet-stream"});
}
bool SystemEditor::startEditor(const FilePath &filePath, QString *errorMessage)
{
Q_UNUSED(errorMessage)
QUrl url;
url.setPath(filePath.toString());
url.setScheme(QLatin1String("file"));
if (!QDesktopServices::openUrl(url)) {
if (errorMessage)
*errorMessage = Tr::tr("Could not open URL %1.").arg(url.toString());
return false;
}
return true;
setEditorStarter([](const FilePath &filePath, QString *errorMessage) {
Q_UNUSED(errorMessage)
QUrl url;
url.setPath(filePath.toString());
url.setScheme(QLatin1String("file"));
if (!QDesktopServices::openUrl(url)) {
if (errorMessage)
*errorMessage = Tr::tr("Could not open URL %1.").arg(url.toString());
return false;
}
return true;
});
}
} // Core::Internal

View File

@@ -7,12 +7,10 @@
namespace Core::Internal {
class SystemEditor : public IExternalEditor
class SystemEditor final : public IEditorFactory
{
public:
explicit SystemEditor();
bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) override;
SystemEditor();
};
} // namespace Core::Internal

View File

@@ -98,13 +98,13 @@ public:
void load();
QList<EditorType *> handlersForMimeType(const Utils::MimeType &mimeType) const;
EditorType *defaultHandlerForMimeType(const Utils::MimeType &mimeType) const;
QList<IEditorFactory *> handlersForMimeType(const Utils::MimeType &mimeType) const;
IEditorFactory *defaultHandlerForMimeType(const Utils::MimeType &mimeType) const;
void resetUserDefaults();
QList<Utils::MimeType> m_mimeTypes;
mutable QHash<Utils::MimeType, QList<EditorType *>> m_handlersByMimeType;
QHash<Utils::MimeType, EditorType *> m_userDefault;
mutable QHash<Utils::MimeType, QList<IEditorFactory *>> m_handlersByMimeType;
QHash<Utils::MimeType, IEditorFactory *> m_userDefault;
};
int MimeTypeSettingsModel::rowCount(const QModelIndex &) const
@@ -139,7 +139,7 @@ QVariant MimeTypeSettingsModel::data(const QModelIndex &modelIndex, int role) co
if (column == 0) {
return type.name();
} else {
EditorType *defaultHandler = defaultHandlerForMimeType(type);
IEditorFactory *defaultHandler = defaultHandlerForMimeType(type);
return defaultHandler ? defaultHandler->displayName() : QString();
}
} else if (role == Qt::EditRole) {
@@ -166,12 +166,12 @@ bool MimeTypeSettingsModel::setData(const QModelIndex &index, const QVariant &va
{
if (role != int(Role::DefaultHandler) || index.column() != 1)
return false;
auto factory = value.value<EditorType *>();
auto factory = value.value<IEditorFactory *>();
QTC_ASSERT(factory, return false);
const int row = index.row();
QTC_ASSERT(row >= 0 && row < m_mimeTypes.size(), return false);
const Utils::MimeType mimeType = m_mimeTypes.at(row);
const QList<EditorType *> handlers = handlersForMimeType(mimeType);
const QList<IEditorFactory *> handlers = handlersForMimeType(mimeType);
QTC_ASSERT(handlers.contains(factory), return false);
if (handlers.first() == factory) // selection is the default anyhow
m_userDefault.remove(mimeType);
@@ -200,18 +200,18 @@ void MimeTypeSettingsModel::load()
endResetModel();
}
QList<EditorType *> MimeTypeSettingsModel::handlersForMimeType(const Utils::MimeType &mimeType) const
QList<IEditorFactory *> MimeTypeSettingsModel::handlersForMimeType(const Utils::MimeType &mimeType) const
{
if (!m_handlersByMimeType.contains(mimeType))
m_handlersByMimeType.insert(mimeType, EditorType::defaultEditorTypes(mimeType));
m_handlersByMimeType.insert(mimeType, IEditorFactory::defaultEditorFactories(mimeType));
return m_handlersByMimeType.value(mimeType);
}
EditorType *MimeTypeSettingsModel::defaultHandlerForMimeType(const Utils::MimeType &mimeType) const
IEditorFactory *MimeTypeSettingsModel::defaultHandlerForMimeType(const Utils::MimeType &mimeType) const
{
if (m_userDefault.contains(mimeType))
return m_userDefault.value(mimeType);
const QList<EditorType *> handlers = handlersForMimeType(mimeType);
const QList<IEditorFactory *> handlers = handlersForMimeType(mimeType);
return handlers.isEmpty() ? nullptr : handlers.first();
}
@@ -808,13 +808,13 @@ QWidget *MimeEditorDelegate::createEditor(QWidget *parent,
void MimeEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
auto box = static_cast<QComboBox *>(editor);
const auto factories = index.model()->data(index, Qt::EditRole).value<QList<EditorType *>>();
for (EditorType *factory : factories)
const auto factories = index.model()->data(index, Qt::EditRole).value<QList<IEditorFactory *>>();
for (IEditorFactory *factory : factories)
box->addItem(factory->displayName(), QVariant::fromValue(factory));
int currentIndex = factories.indexOf(
index.model()
->data(index, int(MimeTypeSettingsModel::Role::DefaultHandler))
.value<EditorType *>());
.value<IEditorFactory *>());
if (QTC_GUARD(currentIndex != -1))
box->setCurrentIndex(currentIndex);
}

View File

@@ -258,63 +258,62 @@ DesignerExternalEditor::DesignerExternalEditor()
setId("Qt.Designer");
setDisplayName(::Core::Tr::tr("Qt Designer"));
setMimeTypes({ProjectExplorer::Constants::FORM_MIMETYPE});
}
bool DesignerExternalEditor::startEditor(const FilePath &filePath, QString *errorMessage)
{
LaunchData data;
setEditorStarter([this](const FilePath &filePath, QString *errorMessage) {
LaunchData data;
// Find the editor binary
if (!getEditorLaunchData(designerBinary, filePath, &data, errorMessage))
return false;
// Find the editor binary
if (!getEditorLaunchData(designerBinary, filePath, &data, errorMessage))
return false;
if (HostOsInfo::isMacHost())
return startEditorProcess(data, errorMessage);
if (HostOsInfo::isMacHost())
return startEditorProcess(data, errorMessage);
/* Qt Designer on the remaining platforms: Uses Designer's own
* Tcp-based communication mechanism to ensure all files are opened
* in one instance (per version). */
/* Qt Designer on the remaining platforms: Uses Designer's own
* Tcp-based communication mechanism to ensure all files are opened
* in one instance (per version). */
// Known one?
const ProcessCache::iterator it = m_processCache.find(data.binary);
if (it != m_processCache.end()) {
// Process is known, write to its socket to cause it to open the file
if (debug)
qDebug() << Q_FUNC_INFO << "\nWriting to socket:" << data.binary << filePath;
QTcpSocket *socket = it.value();
if (!socket->write(filePath.toString().toUtf8() + '\n')) {
*errorMessage = Tr::tr("Qt Designer is not responding (%1).").arg(socket->errorString());
// Known one?
const ProcessCache::iterator it = m_processCache.find(data.binary);
if (it != m_processCache.end()) {
// Process is known, write to its socket to cause it to open the file
if (debug)
qDebug() << Q_FUNC_INFO << "\nWriting to socket:" << data.binary << filePath;
QTcpSocket *socket = it.value();
if (!socket->write(filePath.toString().toUtf8() + '\n')) {
*errorMessage = Tr::tr("Qt Designer is not responding (%1).").arg(socket->errorString());
return false;
}
return true;
}
// No process yet. Create socket & launch the process
QTcpServer server;
if (!server.listen(QHostAddress::LocalHost)) {
*errorMessage = Tr::tr("Unable to create server socket: %1").arg(server.errorString());
return false;
}
return true;
}
// No process yet. Create socket & launch the process
QTcpServer server;
if (!server.listen(QHostAddress::LocalHost)) {
*errorMessage = Tr::tr("Unable to create server socket: %1").arg(server.errorString());
return false;
}
const quint16 port = server.serverPort();
if (debug)
qDebug() << Q_FUNC_INFO << "\nLaunching server:" << port << data.binary << filePath;
// Start first one with file and socket as '-client port file'
// Wait for the socket listening
data.arguments.push_front(QString::number(port));
data.arguments.push_front(QLatin1String("-client"));
const quint16 port = server.serverPort();
if (debug)
qDebug() << Q_FUNC_INFO << "\nLaunching server:" << port << data.binary << filePath;
// Start first one with file and socket as '-client port file'
// Wait for the socket listening
data.arguments.push_front(QString::number(port));
data.arguments.push_front(QLatin1String("-client"));
if (!startEditorProcess(data, errorMessage))
return false;
// Insert into cache if socket is created, else try again next time
if (server.waitForNewConnection(3000)) {
QTcpSocket *socket = server.nextPendingConnection();
socket->setParent(&m_guard);
const QString binary = data.binary;
m_processCache.insert(binary, socket);
auto mapSlot = [binary] { processTerminated(binary); };
QObject::connect(socket, &QAbstractSocket::disconnected, &m_guard, mapSlot);
QObject::connect(socket, &QAbstractSocket::errorOccurred, &m_guard, mapSlot);
}
return true;
if (!startEditorProcess(data, errorMessage))
return false;
// Insert into cache if socket is created, else try again next time
if (server.waitForNewConnection(3000)) {
QTcpSocket *socket = server.nextPendingConnection();
socket->setParent(&m_guard);
const QString binary = data.binary;
m_processCache.insert(binary, socket);
auto mapSlot = [binary] { processTerminated(binary); };
QObject::connect(socket, &QAbstractSocket::disconnected, &m_guard, mapSlot);
QObject::connect(socket, &QAbstractSocket::errorOccurred, &m_guard, mapSlot);
}
return true;
});
}
// Linguist
@@ -331,13 +330,11 @@ LinguistEditor::LinguistEditor()
setId("Qt.Linguist");
setDisplayName(::Core::Tr::tr("Qt Linguist"));
setMimeTypes({ProjectExplorer::Constants::LINGUIST_MIMETYPE});
}
bool LinguistEditor::startEditor(const Utils::FilePath &filePath, QString *errorMessage)
{
LaunchData data;
return getEditorLaunchData(linguistBinary, filePath, &data, errorMessage)
&& startEditorProcess(data, errorMessage);
setEditorStarter([](const FilePath &filePath, QString *errorMessage) {
LaunchData data;
return getEditorLaunchData(linguistBinary, filePath, &data, errorMessage)
&& startEditorProcess(data, errorMessage);
});
}
} // QtSupport::Internal

View File

@@ -9,23 +9,19 @@
namespace QtSupport::Internal {
class DesignerExternalEditor : public Core::IExternalEditor
class DesignerExternalEditor : public Core::IEditorFactory
{
public:
DesignerExternalEditor();
bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) final;
private:
QObject m_guard;
};
class LinguistEditor : public Core::IExternalEditor
class LinguistEditor : public Core::IEditorFactory
{
public:
LinguistEditor();
bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) final;
};
} // QtSupport::Internal