1 /*=============================================================================
2
3 Library: CTK
4
5 Copyright (c) German Cancer Research Center,
6 Division of Medical and Biological Informatics
7
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11
12 http://www.apache.org/licenses/LICENSE-2.0
13
14 Unless required by applicable law or agreed to in writing, software
15 distributed under the License is distributed on an "AS IS" BASIS,
16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 See the License for the specific language governing permissions and
18 limitations under the License.
19
20 =============================================================================*/
21
22 #include "ctkPluginGenerator_p.h"
23 #include "ui_ctkPluginGeneratorMainWindow.h"
24
25 #include <ctkPluginFramework.h>
26 #include <ctkPluginContext.h>
27 #include <ctkServiceReference.h>
28 #include <ctkPluginConstants.h>
29 #include <ctkPluginGeneratorCodeModel.h>
30 #include <ctkPluginGeneratorConstants.h>
31 #include <ctkPluginGeneratorOptionsDialog_p.h>
32 #include <ctkUtils.h>
33
34 #include <QDebug>
35 #include <QListWidgetItem>
36 #include <QDir>
37 #include <QFileSystemModel>
38 #include <QTime>
39 #include <QMessageBox>
40 #include <QSettings>
41
42 #include <stdexcept>
43
44
45 class ctkTemporaryDir
46 {
47 public:
48
create(const QString & path=QString ())49 static QString create(const QString& path = QString())
50 {
51 QString tmpPath = path;
52 if (tmpPath.isEmpty())
53 {
54 tmpPath = "ctkplugingenerator";
55 }
56 tmpPath += "." + QTime::currentTime().toString("hhmmsszzz");
57
58 QDir tmp = QDir::temp();
59 if (!tmp.mkdir(tmpPath))
60 {
61 QString msg = QString("Creating temporary directory ") + tmpPath + " in "
62 + QDir::temp().canonicalPath() + " failed.";
63 throw std::runtime_error(msg.toStdString());
64 }
65
66 tmp.cd(tmpPath);
67
68 return tmp.canonicalPath();
69 }
70 };
71
ctkPluginGenerator(ctkPluginFramework * framework,QWidget * parent)72 ctkPluginGenerator::ctkPluginGenerator(ctkPluginFramework* framework, QWidget *parent) :
73 QMainWindow(parent),
74 framework(framework), ui(new Ui::ctkPluginGeneratorMainWindow),
75 mode(EDIT), previewModel(0)
76 {
77 ui->setupUi(this);
78
79 previewModel = new QFileSystemModel(this);
80 ui->previewTreeView->setModel(previewModel);
81 ui->previewTreeView->hideColumn(1);
82 ui->previewTreeView->hideColumn(2);
83 ui->previewTreeView->hideColumn(3);
84
85 this->setStatusBar(0);
86
87 connect(ui->actionOptions, SIGNAL(triggered(bool)), this, SLOT(menuOptionsTriggered()));
88 connect(ui->generateButton, SIGNAL(clicked()), this, SLOT(generateClicked()));
89 connect(ui->previewButton, SIGNAL(clicked()), this, SLOT(previewClicked()));
90 connect(ui->cancelButton, SIGNAL(clicked()), qApp, SLOT(quit()));
91 connect(ui->uiExtensionList, SIGNAL(itemClicked(QListWidgetItem*)),
92 this, SLOT(extensionItemClicked(QListWidgetItem*)));
93 connect(ui->previewTreeView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(previewIndexChanged(QModelIndex)));
94
95 QList<ctkServiceReference> serviceRefs = framework->getPluginContext()->
96 getServiceReferences("ctkPluginGeneratorAbstractUiExtension");
97 QListIterator<ctkServiceReference> it(serviceRefs);
98 while (it.hasNext())
99 {
100 ctkServiceReference serviceRef = it.next();
101 ctkPluginGeneratorAbstractUiExtension* extension =
102 qobject_cast<ctkPluginGeneratorAbstractUiExtension*>(framework->getPluginContext()->getService(serviceRef));
103 qDebug() << "Service reference found";
104 if (extension)
105 {
106 qDebug() << "inserted";
107 int ranking = serviceRef.getProperty(ctkPluginConstants::SERVICE_RANKING).toInt();
108 if (ranking > 0)
109 {
110 uiExtensionMap.insert(ranking, extension);
111 }
112 else
113 {
114 uiExtensionMap.insert(-1, extension);
115 }
116 }
117 }
118
119 int id = 0;
120 foreach (ctkPluginGeneratorAbstractUiExtension* extension, uiExtensionMap)
121 {
122 idToExtensionMap.insert(id, extension);
123 ui->extensionStack->addWidget(extension->getWidget());
124
125 connect(extension, SIGNAL(errorMessageChanged(QString)), this, SLOT(errorMessageChanged(QString)));
126
127 extension->validate();
128
129 (new QListWidgetItem(extension->getTitle(), ui->uiExtensionList))->setData(Qt::UserRole, id);
130 ++id;
131 }
132
133 ui->uiExtensionList->setCurrentRow(0);
134 extensionClicked(idToExtensionMap[0]);
135 }
136
~ctkPluginGenerator()137 ctkPluginGenerator::~ctkPluginGenerator()
138 {
139 delete ui;
140 if (!previewDir.isEmpty())
141 {
142 ctk::removeDirRecursively(previewDir);
143 }
144 }
145
menuOptionsTriggered()146 void ctkPluginGenerator::menuOptionsTriggered()
147 {
148 ctkPluginGeneratorOptionsDialog optionsDialog;
149 int result = optionsDialog.exec();
150 if (result == QDialog::Accepted && mode == PREVIEW)
151 {
152 QString selPath;
153 QString oldPreviewDir = previewDir;
154 if (!ui->previewTreeView->selectionModel()->selection().isEmpty())
155 {
156 QModelIndex index = ui->previewTreeView->selectionModel()->selectedIndexes().front();
157 selPath = previewModel->data(index, QFileSystemModel::FilePathRole).toString();
158 }
159 if (createPreview())
160 {
161 ui->modeStack->setCurrentWidget(ui->previewPage);
162 ui->previewButton->setText(tr("<< Back"));
163 ui->previewTreeView->expandAll();
164 if (!selPath.isEmpty())
165 {
166 selPath.replace(oldPreviewDir, previewDir);
167 QModelIndex index = previewModel->index(selPath);
168 ui->previewTreeView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
169 previewIndexChanged(index);
170 }
171 }
172 }
173 }
174
previewClicked()175 void ctkPluginGenerator::previewClicked()
176 {
177 if (mode == EDIT)
178 {
179 if (createPreview())
180 {
181 ui->modeStack->setCurrentWidget(ui->previewPage);
182 ui->previewButton->setText(tr("<< Back"));
183 ui->previewTreeView->expandAll();
184 if (!ui->previewTreeView->selectionModel()->selection().isEmpty())
185 {
186 previewIndexChanged(ui->previewTreeView->selectionModel()->selectedIndexes().front());
187 }
188 mode = PREVIEW;
189 }
190 }
191 else
192 {
193 ui->modeStack->setCurrentWidget(ui->editPage);
194 ui->previewButton->setText(tr("Preview >>"));
195 mode = EDIT;
196
197 ctk::removeDirRecursively(previewDir);
198 previewDir.clear();
199 }
200 }
201
generateClicked()202 void ctkPluginGenerator::generateClicked()
203 {
204 try
205 {
206 createPlugin(ui->outputDirButton->directory());
207
208 QMessageBox msgBox;
209 msgBox.setText(tr("Successfully create plugin"));
210 msgBox.setStandardButtons(QMessageBox::Ok);
211 msgBox.setIcon(QMessageBox::Information);
212 msgBox.exec();
213 }
214 catch (const std::runtime_error& error)
215 {
216 QMessageBox msgBox;
217 msgBox.setText(tr("Creating the plugin failed."));
218 msgBox.setInformativeText(QString::fromLatin1(error.what()));
219 msgBox.setStandardButtons(QMessageBox::Ok);
220 msgBox.setIcon(QMessageBox::Critical);
221 msgBox.exec();
222 }
223 }
224
createPlugin(const QString & path)225 QString ctkPluginGenerator::createPlugin(const QString& path)
226 {
227 ctkServiceReference codeModelRef = framework->getPluginContext()->
228 getServiceReference("ctkPluginGeneratorCodeModel");
229
230 ctkPluginGeneratorCodeModel* codeModel =
231 qobject_cast<ctkPluginGeneratorCodeModel*>(framework->getPluginContext()->getService(codeModelRef));
232 codeModel->reset();
233
234 // set global code model info from QSettings object
235 QSettings settings;
236 codeModel->setLicense(settings.value(ctkPluginGeneratorConstants::PLUGIN_LICENSE_MARKER).toString());
237
238 foreach(ctkPluginGeneratorAbstractUiExtension* extension, idToExtensionMap)
239 {
240 extension->updateCodeModel();
241 }
242
243 QString pluginDir = path + "/" + codeModel->getSymbolicName(true);
244 if (!QDir(path).mkdir(codeModel->getSymbolicName(true)))
245 {
246 QString msg(tr("Creating directory \"%1\" failed.").arg(pluginDir));
247 throw std::runtime_error(msg.toStdString());
248 }
249
250 codeModel->create(pluginDir);
251 return pluginDir;
252 }
253
previewIndexChanged(const QModelIndex & index)254 void ctkPluginGenerator::previewIndexChanged(const QModelIndex& index)
255 {
256 QString filePath = previewModel->data(index, QFileSystemModel::FilePathRole).toString();
257 ui->previewTextLabel->setText(QDir(QString(filePath).replace(previewDir, ui->outputDirButton->directory())).absolutePath());
258
259 QFile file(filePath);
260 file.open(QFile::ReadOnly);
261 QTextStream textStream(&file);
262 ui->previewTextEdit->setText(textStream.readAll());
263 }
264
createPreview()265 bool ctkPluginGenerator::createPreview()
266 {
267 try
268 {
269 previewDir = ctkTemporaryDir::create();
270
271 QString tmpPluginDir = createPlugin(previewDir);
272
273 previewModel->setRootPath(tmpPluginDir);
274 ui->previewTreeView->setRootIndex(previewModel->index(previewDir));
275 }
276 catch (const std::runtime_error& error)
277 {
278 QMessageBox msgBox;
279 msgBox.setText(tr("Creating the preview failed."));
280 msgBox.setInformativeText(QString::fromLatin1(error.what()));
281 msgBox.setStandardButtons(QMessageBox::Ok);
282 msgBox.setIcon(QMessageBox::Critical);
283 msgBox.exec();
284 return false;
285 }
286
287 return true;
288 }
289
extensionItemClicked(QListWidgetItem * item)290 void ctkPluginGenerator::extensionItemClicked(QListWidgetItem* item)
291 {
292 ctkPluginGeneratorAbstractUiExtension* extension = idToExtensionMap[item->data(Qt::UserRole).toInt()];
293 extensionClicked(extension);
294 }
295
extensionClicked(ctkPluginGeneratorAbstractUiExtension * extension)296 void ctkPluginGenerator::extensionClicked(ctkPluginGeneratorAbstractUiExtension* extension)
297 {
298 ui->extensionStack->setCurrentWidget(extension->getWidget());
299 ui->extensionMsgLabel->setText(extension->getTitle());
300 this->errorMessageChanged(extension->getErrorMessage());
301 }
302
errorMessageChanged(const QString & errMsg)303 void ctkPluginGenerator::errorMessageChanged(const QString& errMsg)
304 {
305 ui->extensionErrMsgLabel->setText(errMsg);
306
307 bool enableButtons = false;
308 if (errMsg.isEmpty())
309 {
310 enableButtons = true;
311 }
312
313 ui->previewButton->setEnabled(enableButtons);
314 ui->generateButton->setEnabled(enableButtons);
315 }
316