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