Qt Creator Extension System

  • Tutorial

Foreword


Hello. I must admit right away that I started writing this post a long time ago, but there is not enough time to completely finish writing it. Therefore, now I publish it in its current state, but I will postpone the description of the three unfinished sections and try to publish it as a separate post.

Introduction


This is a note in which I would like to describe a little the architecture and extension system of the Qt Creator development environment. Initially, I only intended to translate the document Writing-Qt-Cretor-plugins.pdf , but it so happened that the development of Qt Creator does not stand still and firstly, this document is not so relevant (it’s outdated itself, the API has changed, the pieces of code aren’t full and often do not work), and secondly, from the time of its writing, there are additional Qt Creator extensions that I would like to describe.

Nevertheless, if there wasn’t this document, there wouldn’t have been this note: I took a lot from it, up to the structure of the post, while trying to throw something / replace / add somewhere to make the post relevant for the latter at the moment time version of Qt Creator 2.4.0.

Who can use this document? First of all, these are of course Qt-programmers who chose this IDE as the main development environment.

In addition, thanks to a well-thought-out Qt Creator extension system, this material will be useful for those who are going to create their own development tools, but do not want to start writing them from scratch: a person can disable all the unnecessary Qt Creator extensions and write their own, using ready-made examples in the sources of Qt Creator.

So, what awaits us under the cut (ready sections are marked in bold):

  1. Build Qt Creator
  2. First extension
  3. Adding new menus and menu items
  4. Qt Creator Architecture
  5. Adding a New Editor
  6. Adding a side navigation bar
  7. Adding a page to the settings dialog
  8. Adding a filter to the search dialog
  9. Adding a new project type

Let me remind you that Qt Creator is a cross-platform free IDE for working with the Qt framework developed by Trolltech (Nokia). What doesn’t stop you from making it a simple text editor with syntax highlighting, simple disabling of all extensions. Attention, hundreds of pictures!

1. Build Qt Creator


Assembling the entire IDE is a fairly simple action. First of all, we need to download the source code for the latest version of Qt Creator. this is currently version 2.4. Download the file from qt.nokia.com from the Downloads / Qt-Creator section :
Next, unpack the resulting archive, create the build subdirectory in the source directory, go to it, run qmake and then make:
$ tar -xvf qt-creator-2.4.0-src.tar.gz
$ mkdir qt-creator-2.4.0-src/build
$ cd qt-creator-2.4.0-src/build/
$ qmake ../qtcreator.pro –recursive
$ make

For Windows users, this code can differ only in the last line - instead of make, you will need to call mingw32-make or nmake, depending on the user's preferences.

That's all. You can run Qt Creator from the build / bin directory.
image
It should be noted that this is a very important stage, since if you do not compile Qt Creator from the source, you will not be able to go further and compile and test extensions for it.

2. The first extension


As in many cases, it’s worth starting the Qt Creator extension system by creating a very simple extension. Now we will try to make an extension that does nothing, but using the example of our DoNothing extension, we will learn about the base classes of Qt Creator related to writing extensions and see the “DoNothing” line in the list of available extensions.
image
image

2.1 Creating the Qt Creator Extension Project

Previously, it seems up to version 2.0, to complete this step, it was required to manually create the following files:
  • DoNothingPlugin.pro
  • DoNothingPlugin.h
  • DoNothingPlugin.cpp
  • DoNothingPlugin.pluginspec

Also, then you should either put the extension project in the $$ QT_CREATOR_ROOT / src / plugins directory, or by placing it in another place, you must specify the source directories and builds of Qt Creator in the .pro file. In modern versions of Qt Creator, project files can be placed anywhere, because a new type of wizard appeared - “Qt Creator Module” to create it. The process of creating a new extension at the moment is as follows /
At the very beginning, everything is as usual - we select the type of project to be created,
after we enter the name and path to the directory for its placement
and also the purpose of the assembly, naturally this is Desktop.
image
But after that, a little magic begins - we need to fill out the information specific to the module:
image
Here, in general, everything is more than clear from the picture. The only vital fields here are the path to the source / build directories of Qt Creator. We complete the wizard:
And look at the resulting project structure:
image

2.2 Service files

At first glance, there are a lot of files and directories, but in fact there is nothing to worry about. We will deal with what we saw above and start with the project file:
TARGET = DoNothing
TEMPLATE = lib

DEFINES += DONOTHING_LIBRARY

# DoNothing files

SOURCES += donothingplugin.cpp

HEADERS += donothingplugin.h\
        donothing_global.h\
        donothingconstants.h

OTHER_FILES = DoNothing.pluginspec

# Qt Creator linking

## set the QTC_SOURCE environment variable to override the setting here
QTCREATOR_SOURCES = $$(QTC_SOURCE)
isEmpty(QTCREATOR_SOURCES):QTCREATOR_SOURCES=/home/kafeg/devel/Qt/qt-creator-2.4.0-src

## set the QTC_BUILD environment variable to override the setting here
IDE_BUILD_TREE = $$(QTC_BUILD)
isEmpty(IDE_BUILD_TREE):IDE_BUILD_TREE=/home/kafeg/devel/Qt/qt-creator-2.4.0-src/build

## uncomment to build plugin into user config directory
## <localappdata>/plugins/<ideversion>
##    where <localappdata> is e.g.
##    "%LOCALAPPDATA%\Nokia\qtcreator" on Windows Vista and later
##    "$XDG_DATA_HOME/Nokia/qtcreator" or "~/.local/share/Nokia/qtcreator" on Linux
##    "~/Library/Application Support/Nokia/Qt Creator" on Mac
# USE_USER_DESTDIR = yes

PROVIDER = DoNothingCompany

include($$QTCREATOR_SOURCES/src/qtcreatorplugin.pri)
include($$QTCREATOR_SOURCES/src/plugins/coreplugin/coreplugin.pri)

LIBS += -L$$IDE_PLUGIN_PATH/Nokia

As you can see, in this generated file the name (DoNothing) and the type (library) of the project are described, three header files and one source file are indicated, pluginspec is mentioned. The location of the Qt Creator sources is indicated, commented out instructions allow installing the assembled library not in the Qt Creator directory, but in the local user directory, the provider is described, on which the final location of the library files depends. Finally, the base files for all extensions qtcreatorplugin.pri and coreplugin.pri are included, which are already responsible for the correct linking of our extension with all the necessary libraries.

The following donothing_global.h files :
#ifndef DONOTHING_GLOBAL_H
#define DONOTHING_GLOBAL_H

#include <QtCore/QtGlobal>

#if defined(DONOTHING_LIBRARY)
#  define DONOTHINGSHARED_EXPORT Q_DECL_EXPORT
#else
#  define DONOTHINGSHARED_EXPORT Q_DECL_IMPORT
#endif

#endif // DONOTHING_GLOBAL_H

and donothingconstants.h :
#ifndef DONOTHINGCONSTANTS_H
#define DONOTHINGCONSTANTS_H

namespace DoNothing {
namespace Constants {

const char * const ACTION_ID = "DoNothing.Action";
const char * const MENU_ID = "DoNothing.Menu";

} // namespace DoNothing
} // namespace Constants

#endif // DONOTHINGCONSTANTS_H

Here, I think everything is clear without further explanation. And what is not clear - it will become clear when we will need this code in the future. The list of service files can be completed with the DoNothing.pluginspec.in file:
<plugin name=\"DoNothing\" version=\"0.0.1\" compatVersion=\"0.0.1\">
    <vendor>DoNothingCompany</vendor>
    <copyright>(C) DoNothing Company</copyright>
    <license>DoNothing Company license text here</license>
    <description>DoNothing Company short description of plugin here</description>
    <url>http://www.donothing.com</url>
    <dependencyList>
        <dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/>
    </dependencyList>
</plugin>

This file will eventually be delivered with the binary library file and is a simple description of our extension. And here is what he describes:
  1. The name of the extension to be used in the name of the library that implements it. (In our case, DoNothing.dll on Windows and libDoNothing.so on Unix)
  2. Extension version
  3. Extension version of Qt Creator required
  4. Name of vendor
  5. Copyright
  6. License Text
  7. Description
  8. Vendor url
  9. The list of extensions on which this extension depends.

2.3 Implementation of the extension

To successfully compile an empty project, you need two main implementation files for our extension. What are they like? The main requirement is that the main extension class must be inherited from the IPlugin base class and override some of its methods.
donothingplugin.h
#ifndef DONOTHING_H
#define DONOTHING_H

#include "donothing_global.h"

#include <extensionsystem/iplugin.h>

namespace DoNothing {
namespace Internal {

class DoNothingPlugin : public ExtensionSystem::IPlugin
{
    Q_OBJECT
    
public:
    DoNothingPlugin();
    ~DoNothingPlugin();
    
    bool initialize(const QStringList &arguments, QString *errorString);
    void extensionsInitialized();
    ShutdownFlag aboutToShutdown();
    
private slots:
    void triggerAction();
};

} // namespace Internal
} // namespace DoNothing

#endif // DONOTHING_H

Consider the implementation file for this code in more detail:
#include "donothingplugin.h"
#include "donothingconstants.h"

#include <coreplugin/icore.h>
#include <coreplugin/icontext.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/coreconstants.h>

#include <QtGui/QAction>
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>
#include <QtGui/QMenu>

#include <QtCore/QtPlugin>

using namespace DoNothing::Internal;

DoNothingPlugin::DoNothingPlugin()
{
    // Create your members
}

DoNothingPlugin::~DoNothingPlugin()
{
    // Unregister objects from the plugin manager's object pool
    // Delete members
}

The constructor and destructor are used only to initialize basic variables that are not widgets and / or actions (Action).
bool DoNothingPlugin::initialize(const QStringList &arguments, QString *errorString)
{
    // Register objects in the plugin manager's object pool
    // Load settings
    // Add actions to menus
    // connect to other plugins' signals
    // "In the initialize method, a plugin can be sure that the plugins it
    //  depends on have initialized their members."
    
    Q_UNUSED(arguments)
    Q_UNUSED(errorString)
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    
    QAction *action = new QAction(tr("DoNothing action"), this);
    Core::Command *cmd = am->registerAction(action, Constants::ACTION_ID,
                                            Core::Context(Core::Constants::C_GLOBAL));
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
    connect(action, SIGNAL(triggered()), this, SLOT(triggerAction()));
    
    Core::ActionContainer *menu = am->createMenu(Constants::MENU_ID);
    menu->menu()->setTitle(tr("DoNothing"));
    menu->addAction(cmd);
    am->actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
    
    return true;
}

The initialize () function is called when Qt Creator decides that it is time to initialize the extension. This function is designed to initialize the initial state and register all actions and objects related to the extension in Qt Creator itself.

The function will be called only after all the extensions on which this extension depends are already loaded into memory. In the default code, as can be seen from the example above, adding a new menu item is already described, but we will dwell on this a bit later.

All we need to know now is that if the initialization succeeds, this function should return true, if it fails, return false and write the error message in human language to the errorString variable.
void DoNothingPlugin::extensionsInitialized()
{
    // Retrieve objects from the plugin manager's object pool
    // "In the extensionsInitialized method, a plugin can be sure that all
    //  plugins that depend on it are completely initialized."
}

The extensionsInitialized () method is called after initialization is complete. Serves mainly as an assistant to those extensions that depend on the current.
ExtensionSystem::IPlugin::ShutdownFlag DoNothingPlugin::aboutToShutdown()
{
    // Save settings
    // Disconnect from signals that are not needed during shutdown
    // Hide UI (if you add UI that is not in the main window directly)
    return SynchronousShutdown;
}

The aboutToShutdown () method is called before the extension is unloaded from memory.
void DoNothingPlugin::triggerAction()
{
    QMessageBox::information(Core::ICore::instance()->mainWindow(),
                             tr("Action triggered"),
                             tr("This is an action from DoNothing."));
}

Q_EXPORT_PLUGIN2(DoNothing, DoNothingPlugin)

2.4 Build and Test Extension

To build our extension, just press the key combination Ctrl + R, after which the extension will be assembled and installed in the extension directory of Qt Ceator, but it will not start, since Qt Creator does not know how to run this library. This situation can be fixed on the application launch settings page (Projects -> Launch -> Launch Configuration -> Program): The
result will be the launched Qt Creator, in the list of extensions of which you can see a new line:
A new directory with a couple of files appeared in the directory of all extensions:
kafeg@kafeg-desktop:~/devel/Qt/qt-creator-2.4.0-src/build/lib/qtcreator/plugins/DoNothingCompany$ ls
DoNothing.pluginspec  libDoNothing.so

This way we can add the most basic extension of Qt Creator. Move on.

3 Adding new menus and menu items


In this part, we will learn how to add new menu items to existing menus, and also understand how to create your own menus. But first, look at the contents of the Qt Creator
panel menu : the
panel contains the following default elements:
  • File
    -Create
    -Open
    -Recent files
  • Edit
    -Optional
  • Instruments
  • Window
    - Output Panels
  • reference

All other menu items, for example Debugging, Assembly and Analysis, are implemented in separate extensions and are not part of the default menu set.

Qt developers know that the menus themselves are implemented by a combination of the QMenu and QAction classes, and the QMenuBar class is responsible for displaying them in the form of a panel.

3.1 Core :: ActionManager

The basic part of Qt Creator, this is essentially just an empty window that can load extensions. All the functionality provided by Qt Creator is implemented through its extensions. The main extension of Qt Creator is referred to as "core". Without this extension, Qt Creator is nothing at all.

One of the main components of the core extension is the ActionManager. ActionManager is an object responsible for registering all menus, menu items, and keyboard shortcuts. Actually, if we want to add a new menu item, we must use the ActionManager object. Below, we will figure out how ...
To access the ActionManager object, our code must contain the following:
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
...
Core::ActionManager* am = Core::ICore::instance()->actionManager();

3.2 Core :: ActionContainer

ActionContianer provides menus and menu bars in Qt Creator. Instances of this class are never created directly; they are accessed through the methods ActionManager :: createMenu (), ActionManager :: createMenuBar () and others.

There are instances of ActionContainer associated with all menus by default. To get an instance of ActionContainer, you must use the following code:
#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
Core::ActionManager* am = Core::ICore::instance()->actionManager();
Core::ActionContainer* ac = am->actionContainer( ID );

The following table shows the default IDs that allow you to get ActionContainer instances. IDs are const char * static variables in Core visibility space.
Menu ID
File Core :: Constants :: M_FILE
File -> New Core :: Constants :: M_FILE_NEW
File -> Open Core :: Constants :: M_FILE_OPEN
File -> Recent Files Core :: Constants :: M_FILE_RECENTFILES
Edit Core :: Constants :: M_EDIT
Edit -> Advanced Core :: Constants :: M_EDIT_ADVANCED
Tools Core :: Constants :: M_TOOLS
Window Core :: Constants :: M_WINDOW
Window panes Core :: Constants :: M_WINDOW_PANES
Help Core :: Constants :: M_HELP

For example, if we want to get a pointer to the “Help” menu, then we must use the following code:
#include <coreplugin/coreconstants.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/icore.h>
...
Core::ActionManager* am = Core::ICore::instance()->actionManager();
Core::ActionContainer* ac = am->actionContainer( Core::Constants::M_HELP );

3.3 Adding menus and menu items

Let's see how we can add new menu items. To do this, let's return to our existing function initialize () and look at its implementation in more detail.
bool DoNothingPlugin::initialize(const QStringList &arguments, QString *errorString)
{
    Q_UNUSED(arguments)
    Q_UNUSED(errorString)
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    
    QAction *action = new QAction(tr("DoNothing action"), this);
    Core::Command *cmd = am->registerAction(action, Constants::ACTION_ID,
                                            Core::Context(Core::Constants::C_GLOBAL));
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
    connect(action, SIGNAL(triggered()), this, SLOT(triggerAction()));
    
    Core::ActionContainer *menu = am->createMenu(Constants::MENU_ID);
    menu->menu()->setTitle(tr("DoNothing"));
    menu->addAction(cmd);
    am->actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
    
    return true;
}

The code shown here is now much more understandable. So, we get a pointer to the ActionManager, then create a new menu item and assign it a keyboard shortcut. Then we create our own menu and add to it the item we just created. It is also worth noting that now the constants ACTION_ID and MENU_ID came in handy.

Next, we get a pointer to the ActionContainer tool menu (M_TOOLS) and add our menu to it.
In addition, our keyboard shortcut was entered into the registry of all keyboard shortcuts in the application and is available for change in its settings:
image
It should also be noted that you can add your own menus anywhere. For example, if we would need to add our menu to the main menu bar, then instead of the M_TOOLS constant, we would use the MENU_BAR constant. And to position our menu inside another menu, we should pass an additional parameter to the addMenu () function
am->actionContainer(Core::Constants::MENU_BAR)->addMenu(am->actionContainer(Core::Constants::M_HELP), menu);

3.3 Responding to menu item events

Since menu items are instances of the QAction class, we can use the signals sent by these objects, such as triggered (bool) or toggled (bool) and respond to them by creating slots. Again, an example can be taken from our code above:
...
connect(action, SIGNAL(triggered()), this, SLOT(triggerAction()));
...
void DoNothingPlugin::triggerAction()
{
    QMessageBox::information(Core::ICore::instance()->mainWindow(),
                             tr("Action triggered"),
                             tr("This is an action from DoNothing."));
}

image

4 Qt Creator Architecture


As a rule, each sufficiently large system has its own architecture, having understood which, we will be able to move forward much faster. Qt Creator is simple and in this part we will try to understand its basic architecture, after which we will continue to deal with writing extensions.

4.1 Core Qt Creator

The core of Qt Creator is just an extension manager. All functionality is provided through them.
image


The basic functionality of Qt Creator is implemented in the Core extension (Core :: ICore). We have already touched on this extension in the previous part. Next, we will refer to this base extension as Core. Extension Manager (ExtensionSystem :: PluginManager) provides simple options for the interaction of extensions through hooks that some extensions can provide to others.

4.2 What is an extension?

At the system level, an extension is a shared library (DLL on Windows, SO on Linux, DYLIB on Mac). From the developer's point of view, an extension is a module that:
  1. Inherits and implements the ExtensionSystem :: IPlugin interface. Further in the text we will call a similar class "Plugin Class".
  2. Exports the Plugin Class using the Q_EXPORT_PLUGIN macro.
  3. Provides a (Plugin Name) .pluginspec file containing metadata about the extension.
  4. Provides one or more objects that other extensions might be interested in using.
  5. Checks the availability of one or more objects provided by other extensions.

We have already met with the first three points, but nowhere have we settled on the last two.

4.2.1 How to get a list of available objects?

Available objects are stored in the object pool inside the PluginManager. The allObjects () method from PluginManager returns the entire pool of objects as a list of pointers to a QObject. Below is the code with which we could print a list of available objects:
#include <extensionsystem/pluginmanager.h>
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
QList<QObject*> objects = pm->allObjects();
QListWidget* listWidget = new QListWidget;
Q_FOREACH(QObject* obj, objects)
{
    QString objInfo = QString("%1 (%2)")
    .arg(obj->objectName())
    .arg(obj->metaObject()->className());
    listWidget->addItem(objInfo);
}
listWidget->show();

Thus, after executing this section of code, we will see a long list of objects available to us:
From the names in the list, we can conclude that objects are provided by various extensions. I think now we can give a more precise definition of the term “provided object”:
A provided (exported) object is an instance of the QObject class (or its descendants) provided by one extension and available to other extensions through the pool of objects.

4.2.2 How to export your object to the list of available?

There are three ways for this purpose:
  • IPlugin :: addAutoReleasedObject (QObject *)
  • IPlugin :: addObject (QObject *)
  • PluginManager :: addObject (QObject *)

The IPlugin :: addObject () and IPlugin :: addAutoReleasedObject () methods essentially call the PluginManager :: addObject () method.
IPlugin methods are just for convenience. Extensions are recommended to use them to add objects. The differences between the addAutoReleasedObject () and addObject () methods are that the objects added through the first method will be automatically destroyed and removed from the pool of available objects (in the reverse order) during the destruction of the extension. In addition, at any time, you can call the IPlugin :: removeObject (QObject *) method to manually remove the object from the available pool.

4.2.3 What objects to export?

Extensions can provide (export) any objects, but usually exported objects are structured according to a functional basis. Other Qt Creator functionality is provided through exported interfaces. Here are some of them:
  • Core :: INavigationWidgetFactory
  • Core :: IEditor
  • Core :: IOptionsPage
  • Core :: IOutputPane
  • Core :: IWizard

C ++ developers are accustomed to the fact that classes are usually called interfaces, all methods of which are purely virtual and public. In Qt Creator, interfaces are descendants of QObject that have one or more purely virtual methods.

If the extension has objects that implement any interface, then such an object must be exported. For example, if the extension exports the implementation of the INavigationWidgetFactory interface, then Core will automatically pick up the widget created in this implementation to display it in the navigation panel. As an example, consider adding a simple QTableWidget to the navigation bar through the implementation of the Core :: INavigationWidgetFactory interface.
#include <coreplugin/inavigationwidgetfactory.h>
#include <QTableWidget>

class NavWidgetFactory : public Core::INavigationWidgetFactory
{
public:
    NavWidgetFactory() { }
    ~NavWidgetFactory() { }
    Core::NavigationView createWidget()
    {
        Core::NavigationView view;
        view.widget = new QTableWidget(50, 3);
        return view;
    }
    QString displayName() const
    {
        return "Spreadsheet";
    }
    int priority() const
    {
        return 0;
    }

    QString id() const
    {
        return "Spreadsheet";
    }
};

bool DoNothingPlugin::initialize(const QStringList &arguments, QString *errorString)
{
    ...

    // Provide a navigation widget factory.
    // Qt Creator's navigation widget will automatically
    // hook to our INavigationWidgetFactory implementation, which
    // is the NavWidgetFactory class, and show the QTableWidget
    // created by it in the navigation panel.
    addAutoReleasedObject(new NavWidgetFactory);

    return true;
}

And here is the result:
image

4.2.4 Notifications of newly exported facilities.

When the PluginManager :: addObject () method is called, the PluginManager sends an objectAdded signal (QObject *). This signal can be used to track which objects were newly added while Qt Creator was running.

Obviously, the extension can process this signal only after connecting it to any of its slots, and therefore, it will receive notifications only about objects added after its complete initialization.

Usually, a slot connected to this signal monitors one or more interfaces. For example, this might look like code that follows the appearance of a new object that implements the INavigationWidgetFactory interface.
void Plugin::slotObjectAdded(QObject * obj)
{
    INavigationWidgetFactory *factory
            = Aggregation::query<INavigationWidgetFactory>(obj);
    if(factory)
    {
        // use it here...
    }
}

4.2.4 Object Search

Sometimes an extension may need to find an object in the application that provides certain functionality. Now we know two ways to search for such objects:
  • The PluginManager :: allObjects () method, which returns a list of all available objects.
  • Connect to the PluginManager :: objectAdded () signal, which allows you to receive notification of newly added objects.

Using these methods, the developer can monitor the objects. But what about finding objects? For these purposes, the PluginManager :: getObjects () method exists. For example, if we would like to find all objects that implement the INavigationWidgetFactory interface, then we would use the following code:
ExtensionSystem::PluginManager* pm = ExtensionSystem::PluginManager::instance();
QList<Core::INavigationWidgetFactory*> objects = pm->getObjects<Core::INavigationWidgetFactory>();

4.3 Aggregation of objects

With the help of Aggregation, we can collect several disparate QObjects into one object. Qt Creator typically uses this approach to combine multiple interfaces.
#include <aggregation/aggregate.h>
class Interface1 : public QObject
{
    Q_OBJECT
public:
    Interface1() { }
    ~Interface1() { }
};
class Interface2 : public QObject
{
    Q_OBJECT
public:
    Interface2() { }
    ~Interface2() { }
};
Aggregation::Aggregate bundle;
bundle.add(new Interface1);
bundle.add(new Interface2);

Now the bundle object contains pointers to two objects. To access the interfaces, we can use the following code:
Interface1* iface1Ptr = Aggregation::query<Interface1>( &bundle );
Interface2* iface2Ptr = Aggregation::query<Interface2>( &bundle );

You can include multiple interfaces in one bundle:
Aggregation::Aggregate bundle;
bundle.add(new Interface1);
bundle.add(new Interface2);
bundle.add(new Interface1);
bundle.add(new Interface1);
QList<Interface1*> iface1Ptrs = Aggregation::query_all<Interface1>( &bundle );

And also remove both added interfaces and the bundle itself:
Aggregation::Aggregate* bundle = new Aggregation::Aggregate;
bundle->add(new Interface1);
bundle->add(new Interface2);
Interface1* iface1Ptr = Aggregation::query<Interface1>(bundle);
delete iface1Ptr;
// deletes the bundle and all objects in it
// same as delete bundle

Now it may not be clear why the mention of Aggregation :: Aggregate objects is necessary here, but in the following parts it will be useful to us to simplify the implementation and structure of the described examples.

5 Adding a new editor (Editor)


At a basic level (the same Core Plugin), Qt Creator is nothing more than a text editor. However, with the appropriate extensions, its capabilities are significantly expanded: syntax highlighting for various languages, refactoring and navigation tools using C ++ code, and much more appear in a text editor. There are tools for editing UI (Qt Designer), JS / QML, QRC resource files, project files (PRO / PRI), as well as a hex editor for editing files of type EXE / DLL / SO.

In this part, we will understand how to create editors for our own file formats using the HTML format as an example. When we are done with the editor, we load the HTML file into the editor and see what happens.

5.1 Base classes and interfaces

To add support for the new editor, we need:
  • Implement the Core :: IPlugin class, which exports the implementation of the Core :: IEditorFactory interface. We learned this earlier.
  • Implement the Core :: IEditorFactory interface. This interface will allow you to create the necessary editor, depending on the mime type passed to it.
  • Implement the file editor itself, through the implementation of the Core :: IEditor interface. This implementation will provide a widget that allows you to edit files depending on their type (for example: HTML, ODF and others). the editor must have access to the file that is currently being edited / displayed.
  • Implement the Core :: IFile interface, which will help in the work on loading / saving files on the hard drive.

Now we will consider each of these interfaces.

5.1.1 Interface Core :: IFile.

This interface is an abstract layer between working with files and the user interface. It provides purely virtual methods for loading / saving files, setting the file name, and also helps the application determine the mime type of the file and get the values ​​of certain flags (for example, “modified” and “read-only”). this interface is declared in the src / plugins / coreplugin / ifile.h file :
The question arises - why do I need IFile if QFile already exists? Here's the answer:
  • IFile loads data directly into Core :: IEditor, unlike QFile, which loads a file into QByteArray
  • IFile can send a modified () signal when the user starts editing the file in the editor, but the content remains unchanged. QFile sends bytesWritten () only after the contents of the disk have been changed.
  • IFile monitors whether another file on the disk has been changed by another program and, if so, offers to reload it. QFile similar functionality to anything.

5.1.2 Interface Core :: IEditor.

Implementations of this interface provide editors for various file types. Declared in src / plugins / coreplugin / editormanager / ieditor.h
Core :: IEditor primarily provides access to the following:
  • The editing widget (method Core :: IEditor :: widget ()), which Qt Creator uses to display the contents of the edited file.
  • The Core :: IEditor :: file () method, which is a pointer to Core :: IFile, which Qt Creator can use to work with the disk.
  • An optional toolbar that Qt Creator can display when the editor becomes active.
  • The current cursor position in the file (Core :: IEditor :: currentLine () and Core :: IEditor :: currentColumn ()).
  • The name that appears in the list of open files,

From the screenshot below you can see where what is located.
image

5.1.3 Core :: IEditorFactory Interface

Implementations of this interface show provide methods for instantiating Core :: IEditor for supported mime types. Ad in src / plugins / coreplugin / editormanager / ieditorfactory.h.

The IEditorFactory :: mimeType () method returns the mime type supported by the editor. The IEditorFactory :: createEditor () method creates a specific editor.

5.1.4 Core :: MimeDatabase Class

The Core :: MimeDatabase class stores all mime types supported by Qt Creator. It also helps to get the mime type of a specific file. Example:
#include <coreplugin/mimedatabase.h>
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
Core::MimeType type1 = mdb->findByFile( QFileInfo("C:/Temp/sample.html") );
qDebug("File Type for sample.html = %s", qPrintable(type1.type()));
Core::MimeType type2 = mdb->findByFile( QFileInfo("C:/Temp/TextEdit/Main.cpp") );
qDebug("File Type for Main.cpp = %s", qPrintable(type2.type()));
Core::MimeType type3 = mdb->findByFile( QFileInfo("C:/Temp/TextEdit/TextEdit.pro") );
qDebug("File Type for TextEdit.pro = %s", qPrintable(type3.type()));

And the conclusion:
File Type for sample.html = text/plain
File Type for Main.cpp = text/x-c++src
File Type for TextEdit.pro = text/plain

To work, Core :: MimeDatabase uses the file suffix, patterns, and other magic actions to somehow get the mime type of the file. Let's look at the procedure for opening a file by its mime type using an example:
  1. The user opens the file (File -> Open).
  2. Qt Creator uses Core :: MimeDatabase to determine the mime-type of the selected file.
  3. Qt Creator scans all of Core :: IEditorFactory to determine if they have the mime type they need.
  4. Qt Creator asks the found Core :: IEditorFactory to create an instance of Core :: IEditor.
  5. The widget returned by Core :: IEditor :: widget () is displayed in the workspace.
  6. The method Core :: IEditor :: open () is called with the file name selected in the first step.

5.1.5 Adding a new mime type

If we want to add support for the new editor, we must register the mime type supported by our new editor in Core :: MimeDatabase. There are several ways to do this, now we will look at the simplest one, through creating an XML file. For example, we want to register a text / html mime type and associate it with * .html files. We create an XML file and save it as 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>

Now we can register this mime type through the Core :: MimeDatabase :: addMimeTypes () method:
Core::ICore* core = Core::ICore::instance();
Core::MimeDatabase* mdb = core->mimeDatabase();
QString errMsg;
bool success = mdb->addMimeTypes("text-html-mimetype.xml", errMsg);

As soon as we did this, Qt Creator immediately began to associate all * .html files with the mime type text / html.

5.2 Extend Qt Creator with an HTML editor.

Well, having settled on the theory a bit, we can begin to create an editor that supports viewing / editing HTML files. To implement our plan, we need to create several classes:
Class base class Description
HtmlEditorWidget QTabWidget This will be a widget with two tabs, on the first the final result is shown, and on the second the source of the document.
Htmlfile Core :: IFile Implementation of the IFile interface for HtmlEditorWidget.
Htmleditor Core :: IEditor Implements IEditor to control the HtmlEditorWidget and its interaction with the HtmlFile.
HtmlEditorFactory Core :: IEditorFactory Implements IEditorFactory to create instances of IEditor based on the text / html mime type.
HtmlEditorPlugin Core :: IPlugin Implements IPlugin to communicate all of the above with Qt Creator.

So, we create a new project of the Qt Creator module, call it HTMLEditor for example, and read on.

5.2.1 Implementing the HTML Editor Widget

By default, Qt Creator uses a simple text editor to view HTML files. We are making a two-tab editor, on one tab of which you can view the page, and on the other - edit it.
#ifndef HTMLEDITORWIDGET_H
#define HTMLEDITORWIDGET_H

#include <QTabWidget>
#include <QWebView>
#include <QPlainTextEdit>

struct HtmlEditorWidgetData {
    QWebView *webView;
    QPlainTextEdit *textEdit;
    bool modified;
    QString path;
};

class HtmlEditorWidget : public QTabWidget
{
    Q_OBJECT
public:
    HtmlEditorWidget(QWidget* parent = 0);
    ~HtmlEditorWidget();
    void setContent(const QByteArray& ba, const QString& path=QString());
    QByteArray content() const;
    QString title() const;
protected slots:
    void slotCurrentTabChanged(int tab);
    void slotContentModified();
signals:
    void contentModified();
    void titleChanged(const QString&);
private:
    HtmlEditorWidgetData* d;
};

#endif // HTMLEDITORWIDGET_H

The designer creates two QWebView and QPlainTextEdit widgets, then he adds them as tabs to the current widget and creates three connections:
  1. When the user moves from editing the source to viewing, the contents of the QWeBView should be updated
  2. When the user changes the content, it is necessary to send a signal
  3. Finally, you need to keep an eye on the QWebView header change.

#include "htmleditorwidget.h"
HtmlEditorWidget::HtmlEditorWidget(QWidget* parent)
    :QTabWidget(parent)
{
    d = new HtmlEditorWidgetData;
    d->webView = new QWebView;
    d->textEdit = new QPlainTextEdit;
    addTab(d->webView, "Preview");
    addTab(d->textEdit, "Source");
    //setTabPosition(QTabWidget::South);
    setTabShape(QTabWidget::Triangular);
    d->textEdit->setFont( QFont("Courier", 12) );
    connect(this, SIGNAL(currentChanged(int)),
            this, SLOT(slotCurrentTabChanged(int)));
    connect(d->textEdit, SIGNAL(textChanged()),
            this, SLOT(slotContentModified()));
    connect(d->webView, SIGNAL(titleChanged(QString)),
            this, SIGNAL(titleChanged(QString)));

    d->modified = false;
}

The destructor only deletes the private object:
HtmlEditorWidget::~HtmlEditorWidget()
{
    delete d;
}

The setContent () method sets the contents of webView and textEdit. And the content () method returns content accordingly. The title () method returns a string that will be displayed in the list of open files. Well, the last two methods handle signal connections created in the constructor.
void HtmlEditorWidget::setContent(const QByteArray& ba, const QString& path)
{
    if(path.isEmpty())
        d->webView->setHtml(ba);
    else
        d->webView->setHtml("file:///" + path);
    d->textEdit->setPlainText(ba);
    d->modified = false;
    d->path = path;
}

QByteArray HtmlEditorWidget::content() const
{
    QString htmlText = d->textEdit->toPlainText();
    return htmlText.toAscii();
}

QString HtmlEditorWidget::title() const
{
    return d->webView->title();
}

void HtmlEditorWidget::slotCurrentTabChanged(int tab)
{
    if(tab == 0 && d->modified)
        setContent( content(), d->path );
}

void HtmlEditorWidget::slotContentModified()
{
    d->modified = true;
    emit contentModified();
}

5.2.2 Core :: IFile implementation

We implement this interface in the HtmlFile class. This class will implement several virtual methods from Core :: IFile and will be able to set the modified flag to reflect the status of the document being edited.
#ifndef HTMLFILE_H
#define HTMLFILE_H

#include <coreplugin/ifile.h>
#include "htmleditorconstants.h"

struct HtmlFileData;
class HtmlEditor;
class HtmlEditorWidget;
class HtmlFile : public Core::IFile
{
    Q_OBJECT
public:
    HtmlFile(HtmlEditor* editor, HtmlEditorWidget* editorWidget);
    ~HtmlFile();
    void setModified(bool val=true);
    bool isModified() const;
    QString mimeType() const;
    bool save(QString *errorString, const QString &fileName, bool autoSave);
    bool reload(QString *errorString, ReloadFlag flag, ChangeType type);
    void rename(const QString &newName);
    bool open(const QString &fileName);
    void setFilename(const QString& filename);
    QString fileName() const;
    QString defaultPath() const;
    QString suggestedFileName() const;
    QString fileFilter() const;
    QString fileExtension() const;
    bool isReadOnly() const;
    bool isSaveAsAllowed() const;
    void modified(ReloadBehavior* behavior);

protected slots:
    void modified() { setModified(true); }
private:
    HtmlFileData* d;
};

struct HtmlFileData
{
    HtmlFileData()
        : mimeType(HTMLEditor::Constants::C_HTMLEDITOR_MIMETYPE),
          editorWidget(0), editor(0), modified(false) { }
    const QString mimeType;
    HtmlEditorWidget* editorWidget;
    HtmlEditor* editor;
    QString fileName;
    bool modified;
};

#endif // HTMLFILE_H

In the constructor, we simply set the associations we need. In the destructor, delete the private object.
#include "htmlfile.h"
#include <QFile>
#include <QFileInfo>
#include "htmleditor.h"
#include "htmleditorwidget.h"

HtmlFile::HtmlFile(HtmlEditor* editor, HtmlEditorWidget* editorWidget)
    : Core::IFile(editor)
{
    d = new HtmlFileData;
    d->editor = editor;
    d->editorWidget = editorWidget;
}

HtmlFile::~HtmlFile()
{
    delete d;
}

The setModified () method sets the modified flag and sends a changed () signal
void HtmlFile::setModified(bool val)
{
    if(d->modified == val)
        return;
    d->modified = val;
    emit changed();
}

bool HtmlFile::isModified() const
{
    return d->modified;
}

From here we return the mime type of this class.
QString HtmlFile::mimeType() const
{
    return d->mimeType;
}

The save () method is called when the File -> Save menu item is selected or by the Ctrl + S key combination. It saves the contents of the HtmlEditorWidget (from textEdit) in the current associated file. then the modified flag is set to false.
bool HtmlFile::save(const QString &fileName)
{
    QFile file(fileName);
    if(file.open(QFile::WriteOnly))
    {
        d->fileName = fileName;
        QByteArray content = d->editorWidget->content();
        file.write(content);
        setModified(false);
        return true;
    }
    return false;
}

The open () method is called when selecting File -> Open. either Ctrl + O. Loads the file and passes it to the setContent () function of our edit widget.
bool HtmlFile::open(const QString &fileName)
{
    QFile file(fileName);
    if(file.open(QFile::ReadOnly))
    {
        d->fileName = fileName;
        QString path = QFileInfo(fileName).absolutePath();
        d->editorWidget->setContent(file.readAll(), path);
        d->editor->setDisplayName(d->editorWidget->title());
        return true;
    }
    return false;
}

bool HtmlFile::reload(QString *errorString, ReloadFlag flag, ChangeType type)
{
    return open(d->fileName);
}

void HtmlFile::rename(const QString &newName)
{
    QFile file(d->fileName);
    file.rename(newName);
    setFilename(newName);
}

void HtmlFile::setFilename(const QString& filename)
{
    d->fileName = filename;
}

QString HtmlFile::fileName() const
{
    return d->fileName;
}

QString HtmlFile::defaultPath() const
{
    return QString();
}

QString HtmlFile::suggestedFileName() const
{
    return QString();
}

QString HtmlFile::fileFilter() const
{
    return QString();
}

QString HtmlFile::fileExtension() const
{
    return QString();
}

bool HtmlFile::isReadOnly() const
{
    return false;
}

bool HtmlFile::isSaveAsAllowed() const
{
    return true;
}

void HtmlFile::modified(ReloadBehavior* behavior)
{
    Q_UNUSED(behavior);
}

5.2.3 Core :: IEditor implementation

We are implementing IEditor to give Qt Creator the ability to use our html editor widget and associate it with an HtmlFile.
#ifndef HTMLEDITOR_H
#define HTMLEDITOR_H

#include <coreplugin/editormanager/ieditor.h>
#include <QToolBar>

struct HtmlEditorData;
class HtmlFile;
class HtmlEditorWidget;
class HtmlEditor : public Core::IEditor
{
    Q_OBJECT
public:
    HtmlEditor(HtmlEditorWidget* editorWidget);
    ~HtmlEditor();
    bool createNew(const QString& /*contents*/ = QString());
    QString displayName() const;
    IEditor* duplicate(QWidget* /*parent*/);
    bool duplicateSupported() const;
    Core::IFile* file();
    bool isTemporary() const;
    const char* kind() const;
    bool open(const QString& fileName = QString()) ;
    bool restoreState(const QByteArray& /*state*/);
    QByteArray saveState() const;
    void setDisplayName(const QString &title);
    QToolBar* toolBar();
    // From Core::IContext
    QWidget* widget();
    Core::Context context() const;
    QString id() const;
protected slots:
    void slotTitleChanged(const QString& title)
    { setDisplayName(title); }
private:
    HtmlEditorData* d;
};

The HtmlEditorData object stores pointers to the HtmlEditorWidget and HtmlFile objects, the displayName variable is used to store the name of the editor displayed to the user.
struct HtmlEditorData
{
    HtmlEditorData() : editorWidget(0), file(0) { }
    HtmlEditorWidget* editorWidget;
    QString displayName;
    HtmlFile* file;
    Core::Context context;
};

#endif // HTMLEDITOR_H

The constructor initializes itself, creates an instance of HtmlFile and affixes associations between all three objects. the destructor by tradition only deletes the private object.
#include "htmleditor.h"
#include "htmlfile.h"
#include "htmleditorwidget.h"

HtmlEditor::HtmlEditor(HtmlEditorWidget* editorWidget)
    : Core::IEditor(editorWidget)
{
    d = new HtmlEditorData;
    d->editorWidget = editorWidget;
    d->file = new HtmlFile(this, editorWidget);
    //Core::UniqueIDManager* uidm = Core::UniqueIDManager::instance();
    d->context = *(new Core::Context(HTMLEditor::Constants::C_HTMLEDITOR));
            //<< uidm->uniqueIdentifier(HTMLEditor::Constants::C_HTMLEDITOR);
    connect(d->editorWidget, SIGNAL(contentModified()),
            d->file, SLOT(modified()));
    connect(d->editorWidget, SIGNAL(titleChanged(QString)),
            this, SLOT(slotTitleChanged(QString)));
    connect(d->editorWidget, SIGNAL(contentModified()),
            this, SIGNAL(changed()));
}

HtmlEditor::~HtmlEditor()
{
    delete d;
}

Three simple functions for mapping.
QWidget* HtmlEditor::widget()
{
    return d->editorWidget;
}

Core::Context HtmlEditor::context() const
{
    return d->context;
}

Core::IFile* HtmlEditor::file()
{
    return d->file;
}

The createNew () method resets the contents of HtmlEditorWidget and HtmlFile.
bool HtmlEditor::createNew(const QString& contents)
{
    Q_UNUSED(contents);
    d->editorWidget->setContent(QByteArray());
    d->file->setFilename(QString());
    return true;
}

The open () method passes the file name to open in the HtmlFile.
bool HtmlEditor::open(const QString &fileName)
{
    return d->file->open(fileName);
}

Returns the type of editor.
const char* HtmlEditor::kind() const
{
    return HTMLEditor::Constants::C_HTMLEDITOR;
}

displayName is used to display the file name in the ComboBox.
QString HtmlEditor::displayName() const
{
    return d->displayName;
}

void HtmlEditor::setDisplayName(const QString& title)
{
    if(d->displayName == title)
        return;
    d->displayName = title;
    emit changed();
}

Other methods are not so important.
bool HtmlEditor::duplicateSupported() const
{
    return false;
}

Core::IEditor* HtmlEditor::duplicate(QWidget* parent)
{
    Q_UNUSED(parent);
    return 0;
}

QByteArray HtmlEditor::saveState() const
{
    return QByteArray();
}

bool HtmlEditor::restoreState(const QByteArray& state)
{
    Q_UNUSED(state);
    return false;
}

QToolBar* HtmlEditor::toolBar()
{
    return 0;
}

bool HtmlEditor::isTemporary() const
{
    return false;
}

QString HtmlEditor::id() const
{
    return QString();
}

5.2.4 Implement Core :: IEditorFactory

The HtmlEditorFactory class will implement the Core :: IEditorFactory interface.
#ifndef HTMLEDITORFACTORY_H
#define HTMLEDITORFACTORY_H

#include <coreplugin/editormanager/ieditorfactory.h>
#include <QStringList>
#include "htmleditorplugin.h"

struct HtmlEditorFactoryData;

class HtmlEditorFactory : public Core::IEditorFactory
{
    Q_OBJECT
public:
    HtmlEditorFactory(HTMLEditor::Internal::HTMLEditorPlugin* owner);
    ~HtmlEditorFactory();
    QStringList mimeTypes() const;
    QString kind() const;
    Core::IEditor* createEditor(QWidget* parent);
    Core::IFile* open(const QString &fileName);
private:
    HtmlEditorFactoryData* d;
};

#endif // HTMLEDITORFACTORY_H

The HtmlEditorFactoryData structure contains the private data of the HtmlEditorFactory class. The constructor of this structure initializes it with the mime type contained in HTMLEditor :: Constants :: C_HTMLEDITOR_MIMETYPE. It also initializes the editor type with the value HTMLEditor :: Constants :: C_HTMLEDITOR. All constants are declared in the htmleditorconstants.h file.
#include "htmleditorfactory.h"
#include "htmleditorconstants.h"
#include <coreplugin/editormanager/editormanager.h>
#include "htmleditorwidget.h"
#include <QStringList>
#include <coreplugin/editormanager/ieditor.h>
#include "htmleditor.h"

struct HtmlEditorFactoryData
{
    HtmlEditorFactoryData()
        : kind(HTMLEditor::Constants::C_HTMLEDITOR)
    {
        mimeTypes << QString(HTMLEditor::Constants::C_HTMLEDITOR_MIMETYPE);
    }
    QString kind;
    QStringList mimeTypes;
};

Next, a drop of simple familiar methods.
HtmlEditorFactory::HtmlEditorFactory(HTMLEditor::Internal::HTMLEditorPlugin* owner)
    :Core::IEditorFactory(owner)
{
    d = new HtmlEditorFactoryData;
}

HtmlEditorFactory::~HtmlEditorFactory()
{
    delete d;
}

QStringList HtmlEditorFactory::mimeTypes() const
{
    return d->mimeTypes;
}

QString HtmlEditorFactory::kind() const
{
    return d->kind;
}

The open () method passes the file name to the editor manager, who in turn either creates and opens a new editor, or opens one of the existing ones.
Core::IFile* HtmlEditorFactory::open(const QString& fileName)
{
    Core::EditorManager* em = Core::EditorManager::instance();
    Core::IEditor* iface = em->openEditor(fileName, d->kind);
    return iface ? iface->file() : 0;
}

This method returns an instance of the HtmlEditor class.
Core::IEditor* HtmlEditorFactory::createEditor(QWidget* parent)
{
    HtmlEditorWidget* editorWidget = new HtmlEditorWidget(parent);
    return new HtmlEditor(editorWidget);
}

5.2.5 Main extension class

We implement the HtmlEditorPlugin class using the knowledge that we gained in the second part. The truth is a little change the initialize () method.
bool HTMLEditorPlugin::initialize(const QStringList &arguments, QString *errorString)
{
    Q_UNUSED(arguments)
    Q_UNUSED(errorString)

    Core::ICore* core = Core::ICore::instance();
    Core::MimeDatabase* mdb = core->mimeDatabase();

    QString *errMsg = new QString();
    if(!mdb->addMimeTypes(":/text-html-mimetype.xml", errMsg))
        return false;
    addAutoReleasedObject(new HtmlEditorFactory(this));

    return true;
}

6 Adding a side navigation bar


The navigation bar in Qt Creator is an area that may contain components such as:
  • Projects
  • Open documents
  • Bookmarks
  • File system
  • Class Overview
  • Overview
  • Type hierarchy
  • other...

In addition, we can simultaneously use not one but several navigation panels.
image

Remember, we already tried to add a navigation bar containing a label in one of the previous parts? Right now we will do almost the same thing, but a little more complicated task.

6.1 Core :: INavigationWidgetFactory

So, as far as we remember, in order to add a new navigation panel, we need to create our own implementation of the Core :: INavigationWidgetFactory interface located in the plugins / corelib / inavigationwidgetfactory.h file.

6.2 Preparing the widget for the panel

Re-create the Qt Creator module project. And we will call it FTPBrowser. Yes, precisely because there was this example in the original document. we should also look at this link to download the FtpDirModel class.

Now we create a new form class in our project and bring the form to this form:
When the user enters the path to the FTP directory in the FTP Path line and presses Go, the contents of this FTP are immediately uploaded to QTreeView located a bit lower. For example, I named this widget FTPBrowserForm. I will not give the source of the form, it can then be taken along with all the others. We proceed immediately to the implementation of the interface.
#include <coreplugin/inavigationwidgetfactory.h>
class FtpViewNavigationWidgetFactory
        : public Core::INavigationWidgetFactory
{
public:
    FtpViewNavigationWidgetFactory() { }
    ~FtpViewNavigationWidgetFactory() { }
    Core::NavigationView createWidget();
    QString displayName() const
    {
        return "FTP View";
    }
    int priority() const
    {
        return 0;
    }

    QString id() const
    {
        return "Spreadsheet";
    }
};

The createWidget () method only creates a new widget and returns a pointer to it. The displayName () method returns a string to display to the user.
Core::NavigationView FtpViewNavigationWidgetFactory::createWidget()
{
    Core::NavigationView view;
    view.widget = new FTPBrowserForm;
    return view;
}

Here's what we got:
image

6.3 Saving and restoring panels

Sometimes it may be necessary to save / load the state of the panels after restarting Qt Creator. For these purposes, there are two special virtual functions in the INavigationWidgetFactory interface:
void saveSettings(int position, QWidget *widget);
void restoreSettings(int position, QWidget *widget);

By overriding these two methods, we can easily control the state of our widgets. Here is a small example from our code:
void FtpViewNavigationWidgetFactory::saveSettings(int position, QWidget *widget)
{
    FTPBrowserForm* ftpExp = qobject_cast<FTPBrowserForm*>(widget);
    if(!ftpExp)
        return;
    QSettings *settings = Core::ICore::instance()->settings();
    settings->setValue("FtpView.URL", ftpExp->url().toString());
}

void FtpViewNavigationWidgetFactory::restoreSettings(int position, QWidget *widget)
{
    FTPBrowserForm* ftpExp = qobject_cast<FTPBrowserForm*>(widget);
    if(!ftpExp)
    return;
    QSettings *settings = Core::ICore::instance()->settings();
    QString urlStr = settings->value("FtpView.URL").toString();
    ftpExp->setUrl( QUrl(urlStr) );
}

Two simple functions whose parameters are the position and pointer to the panel widget.

Fuf ... that's all for now ... the rest of the article will be published ... when and if it will be added =)