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 test 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    explicit SampleViewPlugin();
64
65#include "filehandlinginterface.inl"
66#include "i18ninterface.inl"
67#include "plugininterface.inl"
68#include "viewinterface.inl"
69
70private:
71    SampleViewWidget *mViewWidget;
72
73    QString mFileName;
74};
75
76//==============================================================================
77
78} // namespace SampleView
79} // 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 65-68) 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    Descriptions descriptions;
43
44    descriptions.insert("en", QString::fromUtf8("a plugin that provides a test view."));
45    descriptions.insert("fr", QString::fromUtf8("une extension qui fournit une vue de test."));
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:

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

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 64-71) saveFile() (lines 75-86), fileOpened() (lines 90-95), fileModified() (lines 111-116) and fileSaved() (lines 120-125). 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 99-107) and fileReloaded() (lines 129-136) methods. The same holds true if the current file gets renamed, in which case we also want to update mFileName (see fileRenamed(); lines 140-152). Finally, we want to reset mFileName if the current file gets closed (see fileClosed(); lines 156-163).

Next, we have the Internationalisation interface:

165//==============================================================================
166// I18n interface
167//==============================================================================
168
169void SampleViewPlugin::retranslateUi()
170{
171    // Retranslate our view widget, if needed
172
173    if (!mFileName.isEmpty()) {
174        mViewWidget->retranslateUi();
175    }
176}
177
178//==============================================================================

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:

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

The only method of interest to our plugin is initializePlugin() (lines 204-214), 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:

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

Our plugin provides a view, so OpenCOR needs to know about its name (see viewName(); lines 325-330), its type (see viewMode(); lines 263-268), the MIME types it supports (see viewMimeTypes(); lines 272-277), the MIME type supported by a given file (see viewMimeType(); lines 281-288), the default file extension it supports (see viewDefaultFileExtension(); lines 292-297) and whether it needs a special tab icon (see fileTabIcon(); lines 334-341). OpenCOR also needs to know the widget that is used for the view and this for a given file (see viewWidget(); lines 301-310). 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 314-321), 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    mFileName(QString())
 47{
 48    // Delete the layout that comes with ViewWidget
 49
 50    delete layout();
 51
 52    // Set up the GUI
 53
 54    mGui->setupUi(this);
 55}
 56
 57//==============================================================================
 58
 59SampleViewWidget::~SampleViewWidget()
 60{
 61    // Delete the GUI
 62
 63    delete mGui;
 64}
 65
 66//==============================================================================
 67
 68void SampleViewWidget::retranslateUi()
 69{
 70    // Retranslate our GUI
 71
 72    mGui->retranslateUi(this);
 73
 74    // Update ourself too since some widgets will have been reset following the
 75    // retranslation (e.g. mGui->fileNameValue)
 76
 77    update(mFileName);
 78}
 79
 80//==============================================================================
 81
 82QWidget * SampleViewWidget::widget(const QString &pFileName)
 83{
 84    Q_UNUSED(pFileName)
 85
 86    // Return the requested (self) widget
 87
 88    return this;
 89}
 90
 91//==============================================================================
 92
 93void SampleViewWidget::update(const QString &pFileName)
 94{
 95    // Keep track of the given file name
 96
 97    mFileName = pFileName;
 98
 99    // Initialise our GUI with some information about the given file
100
101    mGui->fileNameValue->setText(pFileName);
102
103    Core::FileManager *fileManagerInstance = Core::FileManager::instance();
104
105    mGui->lockedValue->setText(fileManagerInstance->isLocked(pFileName)?tr("Yes"):tr("No"));
106
107    QString sha1Value = fileManagerInstance->sha1(pFileName);
108
109    mGui->sha1Value->setText(sha1Value.isEmpty()?"???":sha1Value);
110    mGui->sizeValue->setText(Core::sizeAsString(quint64(QFile(pFileName).size())));
111}
112
113//==============================================================================
114
115} // namespace SampleView
116} // namespace OpenCOR

retranslateUi() (lines 68-78) retranslates our view while widget() returns it (lines 82-89) and update() updates its contents (lines 93-111).