The purpose of this plugin is to provide a view that gives some information about the current file.
i18n
└─ SampleView_fr.ts
res
└─ SampleView_i18n.qrc.in
src
├─ sampleviewplugin.cpp
├─ sampleviewplugin.h
├─ sampleviewplugin.json
├─ sampleviewwidget.cpp
├─ sampleviewwidget.h
└─ sampleviewwidget.ui
CMakeLists.txt
Our plugin is part of the Sample category, which means that its code can be found under [OpenCOR]/src/plugins/sample/SampleView/
.
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.
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).
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).
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.
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).