Sample view

The purpose of this plugin is to provide a view that gives some information about the current file.

File structure

i18n
 └─ SampleView_fr.ts
res
 └─ SampleView_i18n.qrc.in
src
 ├─ sampleviewplugin.cpp
 ├─ sampleviewplugin.h
 ├─ sampleviewplugin.json
 ├─ sampleviewwidget.cpp
 ├─ sampleviewwidget.h
 └─ sampleviewwidget.ui
CMakeLists.txt

Category

Our plugin is part of the Sample category, which means that its code can be found under [OpenCOR]/src/plugins/sample/SampleView/.

Interfaces

We want our plugin to interact with OpenCOR. This means that it needs to implement some interfaces.

More specifically, we want our plugin to be a view, so we need to implement both the Plugin and View interfaces. Considering the purpose of our plugin, we also need to implement the File handling interface. While we are at it, we might also internationalise our plugin, which means also implementing the Internationalisation interface.

CMake project

As for the Sample plugin, our plugin has a CMakeLists.txt file, which contents is:

 1project(SampleViewPlugin)
 2
 3# Add the plugin
 4
 5add_plugin(SampleView
 6    SOURCES
 7        ../../filehandlinginterface.cpp
 8        ../../i18ninterface.cpp
 9        ../../plugininfo.cpp
10        ../../plugininterface.cpp
11        ../../viewinterface.cpp
12
13        src/sampleviewplugin.cpp
14        src/sampleviewwidget.cpp
15    UIS
16        src/sampleviewwidget.ui
17    PLUGINS
18        Core
19)

The interfaces our plugin implements come with a .cpp file, so we reference them (lines 7, 8, 10, and 11). Then, our plugin needs the Core plugin, so it is referenced (line 18) using the PLUGINS keyword (line 17). Our plugin provides OpenCOR with a view, which is implemented using various files (lines 14 and 16). One of those files is a .ui file, which is referenced using the UIS keyword (line 15).

Plugin information

Our plugin information can be found in sampleviewplugin.cpp, sampleviewplugin.h and sampleviewplugin.json. Starting with sampleviewplugin.h, its contents is:

28#include "filehandlinginterface.h"
29#include "i18ninterface.h"
30#include "plugininfo.h"
31#include "plugininterface.h"
32#include "viewinterface.h"
33
34//==============================================================================
35
36namespace OpenCOR {
37namespace SampleView {
38
39//==============================================================================
40
41PLUGININFO_FUNC SampleViewPluginInfo();
42
43//==============================================================================
44
45class SampleViewWidget;
46
47//==============================================================================
48
49class SampleViewPlugin : public QObject, public FileHandlingInterface,
50                         public I18nInterface, public PluginInterface,
51                         public ViewInterface
52{
53    Q_OBJECT
54
55    Q_PLUGIN_METADATA(IID "OpenCOR.SampleViewPlugin" FILE "sampleviewplugin.json")
56
57    Q_INTERFACES(OpenCOR::FileHandlingInterface)
58    Q_INTERFACES(OpenCOR::I18nInterface)
59    Q_INTERFACES(OpenCOR::PluginInterface)
60    Q_INTERFACES(OpenCOR::ViewInterface)
61
62public:
63#include "filehandlinginterface.inl"
64#include "i18ninterface.inl"
65#include "plugininterface.inl"
66#include "viewinterface.inl"
67
68private:
69    SampleViewWidget *mViewWidget = nullptr;
70
71    QString mFileName;
72};
73
74//==============================================================================
75
76} // namespace SampleView
77} // namespace OpenCOR

As mentioned above, our plugin implements some interfaces, which means that their header file is included (lines 28, 29, 31 and 32). It also means that our plugin class inherits from those interfaces (lines 49-51), as well as makes calls to the Q_INTERFACES() macro to let Qt know which interfaces it implements (lines 57-60). Finally, we include the inline files (lines 63-66) that declare various methods that must be implemented by our plugin (see below). (The rest of the class definition is specific to our plugin and is discussed below.)

The C function that is used by OpenCOR to retrieve some basic information about our plugin can be found in sampleviewplugin.cpp:

40PLUGININFO_FUNC SampleViewPluginInfo()
41{
42    static const Descriptions descriptions = {
43                                                 { "en", QString::fromUtf8("a plugin that provides a test view.") },
44                                                 { "fr", QString::fromUtf8("une extension qui fournit une vue de test.") }
45                                             };
46
47    return new PluginInfo(PluginInfo::Category::Sample, true, false,
48                          { "Core" },
49                          descriptions);
50}

As can be seen, our plugin is selectable by the user, but it does not offer CLI support (line 47). It also has a direct dependency on the Core plugin (line 48).

Interfaces implementation

The implementation of the interfaces’ various methods can be found in sampleviewplugin.cpp. The methods are grouped by interface and are ordered alphabetically.

We start with the File handling interface:

 52//==============================================================================
 53// File handling interface
 54//==============================================================================
 55
 56bool SampleViewPlugin::importFile(const QString &pFileName)
 57{
 58    Q_UNUSED(pFileName)
 59
 60    // We don't handle this interface...
 61
 62    return false;
 63}
 64
 65//==============================================================================
 66
 67bool SampleViewPlugin::saveFile(const QString &pOldFileName,
 68                                const QString &pNewFileName,
 69                                bool &pNeedFeedback)
 70{
 71    Q_UNUSED(pOldFileName)
 72    Q_UNUSED(pNewFileName)
 73    Q_UNUSED(pNeedFeedback)
 74
 75    // We don't handle this interface...
 76
 77    return false;
 78}
 79
 80//==============================================================================
 81
 82void SampleViewPlugin::fileOpened(const QString &pFileName)
 83{
 84    Q_UNUSED(pFileName)
 85
 86    // We don't handle this interface...
 87}
 88
 89//==============================================================================
 90
 91void SampleViewPlugin::filePermissionsChanged(const QString &pFileName)
 92{
 93    // The given file has had its permissions changed, so update our view
 94    // widget, if needed
 95
 96    if (pFileName == mFileName) {
 97        mViewWidget->update(pFileName);
 98    }
 99}
100
101//==============================================================================
102
103void SampleViewPlugin::fileModified(const QString &pFileName)
104{
105    Q_UNUSED(pFileName)
106
107    // We don't handle this interface...
108}
109
110//==============================================================================
111
112void SampleViewPlugin::fileSaved(const QString &pFileName)
113{
114    Q_UNUSED(pFileName)
115
116    // We don't handle this interface...
117}
118
119//==============================================================================
120
121void SampleViewPlugin::fileReloaded(const QString &pFileName)
122{
123    // The given file has been reloaded, so update our view widget, if needed
124
125    if (pFileName == mFileName) {
126        mViewWidget->update(pFileName);
127    }
128}
129
130//==============================================================================
131
132void SampleViewPlugin::fileRenamed(const QString &pOldFileName,
133                                   const QString &pNewFileName)
134{
135    Q_UNUSED(pOldFileName)
136
137    // The given file has been renamed, so update our view widget, if needed
138
139    if (pOldFileName == mFileName) {
140        mFileName = pNewFileName;
141
142        mViewWidget->update(pNewFileName);
143    }
144}
145
146//==============================================================================
147
148void SampleViewPlugin::fileClosed(const QString &pFileName)
149{
150    // The given file has been closed, so update our internals, if needed
151
152    if (pFileName == mFileName) {
153        mFileName = QString();
154    }
155}
156
157//==============================================================================

Our plugin provides a view and, as such, should at least handle some of the File handling interface’s methods. Here, we want our plugin to provide some information about the current file, so we do not need to implement importFile() (lines 56-63) saveFile() (lines 67-78), fileOpened() (lines 82-87), fileModified() (lines 103-108), and fileSaved() (lines 112-117). On the other hand, should the current file have its permissions changed or be renamed, then we want to update the information presented in our view. We do this by implementing the filePermissionsChanged() (lines 91-99) and fileReloaded() (lines 128-128) methods. The same holds true if the current file gets renamed, in which case we also want to update mFileName (see fileRenamed(); lines 132-144). Finally, we want to reset mFileName if the current file gets closed (see fileClosed(); lines 148-155).

Next, we have the Internationalisation interface:

157//==============================================================================
158// I18n interface
159//==============================================================================
160
161void SampleViewPlugin::retranslateUi()
162{
163    // Retranslate our view widget, if needed
164
165    if (!mFileName.isEmpty()) {
166        mViewWidget->retranslateUi();
167    }
168}
169
170//==============================================================================

If some information is being shown for a file, then we ask our view to retranslate itself.

After the Internationalisation interface, we have the Plugin interface:

170//==============================================================================
171// Plugin interface
172//==============================================================================
173
174bool SampleViewPlugin::definesPluginInterfaces()
175{
176    // We don't handle this interface...
177
178    return false;
179}
180
181//==============================================================================
182
183bool SampleViewPlugin::pluginInterfacesOk(const QString &pFileName,
184                                          QObject *pInstance)
185{
186    Q_UNUSED(pFileName)
187    Q_UNUSED(pInstance)
188
189    // We don't handle this interface...
190
191    return false;
192}
193
194//==============================================================================
195
196void SampleViewPlugin::initializePlugin()
197{
198    // Create our Sample view widget
199
200    mViewWidget = new SampleViewWidget(Core::mainWindow());
201
202    // Hide our Sample view widget since it may not initially be shown in our
203    // central widget
204
205    mViewWidget->setVisible(false);
206}
207
208//==============================================================================
209
210void SampleViewPlugin::finalizePlugin()
211{
212    // We don't handle this interface...
213}
214
215//==============================================================================
216
217void SampleViewPlugin::pluginsInitialized(const Plugins &pLoadedPlugins)
218{
219    Q_UNUSED(pLoadedPlugins)
220
221    // We don't handle this interface...
222}
223
224//==============================================================================
225
226void SampleViewPlugin::loadSettings(QSettings &pSettings)
227{
228    Q_UNUSED(pSettings)
229
230    // We don't handle this interface...
231}
232
233//==============================================================================
234
235void SampleViewPlugin::saveSettings(QSettings &pSettings) const
236{
237    Q_UNUSED(pSettings)
238
239    // We don't handle this interface...
240}
241
242//==============================================================================
243
244void SampleViewPlugin::handleUrl(const QUrl &pUrl)
245{
246    Q_UNUSED(pUrl)
247
248    // We don't handle this interface...
249}
250
251//==============================================================================

The only method of interest to our plugin is initializePlugin() (lines 196-206), which is where we initialise mViewWidget, our view. All the other methods (definesPluginInterfaces(), pluginInterfacesOk(), finalizePlugin(), pluginsInitialized(), loadSettings(), saveSettings() and handleUrl()) are left empty.

Finally, we have the View interface:

251//==============================================================================
252// View interface
253//==============================================================================
254
255ViewInterface::Mode SampleViewPlugin::viewMode() const
256{
257    // Return our mode
258
259    return ViewInterface::Mode::Sample;
260}
261
262//==============================================================================
263
264QStringList SampleViewPlugin::viewMimeTypes() const
265{
266    // Return the MIME types we support, i.e. any in our case
267
268    return {};
269}
270
271//==============================================================================
272
273QString SampleViewPlugin::viewMimeType(const QString &pFileName) const
274{
275    Q_UNUSED(pFileName)
276
277    // Return the MIME type for the given file
278
279    return {};
280}
281
282//==============================================================================
283
284QString SampleViewPlugin::viewDefaultFileExtension() const
285{
286    // Return the default file extension we support
287
288    return {};
289}
290
291//==============================================================================
292
293QWidget * SampleViewPlugin::viewWidget(const QString &pFileName)
294{
295    // Update and return our Sample view widget using the given file
296
297    mFileName = pFileName;
298
299    mViewWidget->update(pFileName);
300
301    return mViewWidget;
302}
303
304//==============================================================================
305
306void SampleViewPlugin::removeViewWidget(const QString &pFileName)
307{
308    Q_UNUSED(pFileName)
309
310    // Reset our internals
311
312    mFileName = QString();
313}
314
315//==============================================================================
316
317QString SampleViewPlugin::viewName() const
318{
319    // Return our Sample view's name
320
321    return tr("Sample");
322}
323
324//==============================================================================
325
326QIcon SampleViewPlugin::fileTabIcon(const QString &pFileName) const
327{
328    Q_UNUSED(pFileName)
329
330    // We don't handle this interface...
331
332    return {};
333}
334
335//==============================================================================

Our plugin provides a view, so OpenCOR needs to know about its name (see viewName(); lines 317-322), its type (see viewMode(); lines 255-260), the MIME types it supports (see viewMimeTypes(); lines 264-269), the MIME type supported by a given file (see viewMimeType(); lines 273-280), the default file extension it supports (see viewDefaultFileExtension(); lines 284-289), and whether it needs a special tab icon (see fileTabIcon(); lines 326-333). OpenCOR also needs to know the widget that is used for the view and this for a given file (see viewWidget(); lines 293-302). Note that our plugin uses only one view widget (and updates its contents based on the file that is currently active), but it might perfectly use one per file. Finally, our plugin needs to handle the case where a view widget is to be removed (see removeViewWidget(); lines 306-313), which happens whenever a file gets closed.

Plugin specific

Some extra work is needed to get our plugin to do what we want, and this is done via the SampleViewWidget class in sampleviewwidget.h:

28#include "viewwidget.h"
29
30//==============================================================================
31
32namespace Ui {
33    class SampleViewWidget;
34} // namespace Ui
35
36//==============================================================================
37
38namespace OpenCOR {
39namespace SampleView {
40
41//==============================================================================
42
43class SampleViewWidget : public Core::ViewWidget
44{
45    Q_OBJECT
46
47public:
48    explicit SampleViewWidget(QWidget *pParent);
49    ~SampleViewWidget() override;
50
51    void retranslateUi() override;
52
53    QWidget * widget(const QString &pFileName) override;
54
55    void update(const QString &pFileName);
56
57private:
58    Ui::SampleViewWidget *mGui;
59
60    QString mFileName;
61};
62
63//==============================================================================
64
65} // namespace SampleView
66} // namespace OpenCOR

SampleViewWidget inherits from Core::ViewWidget, which is defined in the Core plugin and is an extended version of Qt’s QWidget class (line 43). It requires implementing the widget() method, which purpose is to return the widget that is to be shown in the view (line 53). In the present case, it is SampleViewWidget itself. Otherwise, SampleViewWidget also comes with a GUI file, which describes the layout of our plugin window (sampleviewwidget.ui). The update() method is used by our plugin to update the contents of our view using information about the given file (line 55).

The implementation of SampleViewWidget can be found in sampleviewwidget.cpp:

 24#include "corecliutils.h"
 25#include "filemanager.h"
 26#include "sampleviewwidget.h"
 27
 28//==============================================================================
 29
 30#include "ui_sampleviewwidget.h"
 31
 32//==============================================================================
 33
 34#include <QFile>
 35
 36//==============================================================================
 37
 38namespace OpenCOR {
 39namespace SampleView {
 40
 41//==============================================================================
 42
 43SampleViewWidget::SampleViewWidget(QWidget *pParent) :
 44    ViewWidget(pParent),
 45    mGui(new Ui::SampleViewWidget)
 46{
 47    // Delete the layout that comes with ViewWidget
 48
 49    delete layout();
 50
 51    // Set up the GUI
 52
 53    mGui->setupUi(this);
 54}
 55
 56//==============================================================================
 57
 58SampleViewWidget::~SampleViewWidget()
 59{
 60    // Delete the GUI
 61
 62    delete mGui;
 63}
 64
 65//==============================================================================
 66
 67void SampleViewWidget::retranslateUi()
 68{
 69    // Retranslate our GUI
 70
 71    mGui->retranslateUi(this);
 72
 73    // Update ourself too since some widgets will have been reset following the
 74    // retranslation (e.g., mGui->fileNameValue)
 75
 76    update(mFileName);
 77}
 78
 79//==============================================================================
 80
 81QWidget * SampleViewWidget::widget(const QString &pFileName)
 82{
 83    Q_UNUSED(pFileName)
 84
 85    // Return the requested (self) widget
 86
 87    return this;
 88}
 89
 90//==============================================================================
 91
 92void SampleViewWidget::update(const QString &pFileName)
 93{
 94    // Keep track of the given file name
 95
 96    mFileName = pFileName;
 97
 98    // Initialise our GUI with some information about the given file
 99
100    mGui->fileNameValue->setText(pFileName);
101
102    Core::FileManager *fileManagerInstance = Core::FileManager::instance();
103
104    mGui->lockedValue->setText(fileManagerInstance->isLocked(pFileName)?tr("Yes"):tr("No"));
105
106    QString sha1Value = fileManagerInstance->sha1(pFileName);
107
108    mGui->sha1Value->setText(sha1Value.isEmpty()?"???":sha1Value);
109    mGui->sizeValue->setText(Core::sizeAsString(quint64(QFile(pFileName).size())));
110}
111
112//==============================================================================
113
114} // namespace SampleView
115} // namespace OpenCOR

retranslateUi() (lines 67-77) retranslates our view while widget() returns it (lines 81-88) and update() updates its contents (lines 92-110).