1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "simpleprojectwizard.h"
27
28 #include "projectexplorerconstants.h"
29
30 #include <app/app_version.h>
31
32 #include <coreplugin/basefilewizard.h>
33 #include <coreplugin/icore.h>
34
35 #include <cmakeprojectmanager/cmakeprojectconstants.h>
36 #include <projectexplorer/projectexplorerconstants.h>
37 #include <projectexplorer/customwizard/customwizard.h>
38 #include <projectexplorer/selectablefilesmodel.h>
39 #include <qmakeprojectmanager/qmakeprojectmanagerconstants.h>
40
41 #include <utils/algorithm.h>
42 #include <utils/fileutils.h>
43 #include <utils/filewizardpage.h>
44 #include <utils/mimetypes/mimedatabase.h>
45 #include <utils/wizard.h>
46
47 #include <QApplication>
48 #include <QComboBox>
49 #include <QDebug>
50 #include <QDir>
51 #include <QFileInfo>
52 #include <QLineEdit>
53 #include <QPainter>
54 #include <QPixmap>
55 #include <QStyle>
56 #include <QVBoxLayout>
57 #include <QWizardPage>
58
59 using namespace Core;
60 using namespace Utils;
61
62 namespace ProjectExplorer {
63 namespace Internal {
64
65 class SimpleProjectWizardDialog;
66
67 class FilesSelectionWizardPage : public QWizardPage
68 {
69 Q_OBJECT
70
71 public:
72 FilesSelectionWizardPage(SimpleProjectWizardDialog *simpleProjectWizard);
isComplete() const73 bool isComplete() const override { return m_filesWidget->hasFilesSelected(); }
74 void initializePage() override;
cleanupPage()75 void cleanupPage() override { m_filesWidget->cancelParsing(); }
selectedFiles() const76 FilePaths selectedFiles() const { return m_filesWidget->selectedFiles(); }
selectedPaths() const77 FilePaths selectedPaths() const { return m_filesWidget->selectedPaths(); }
qtModules() const78 QString qtModules() const { return m_qtModules; }
buildSystem() const79 QString buildSystem() const { return m_buildSystem; }
80
81 private:
82 SimpleProjectWizardDialog *m_simpleProjectWizardDialog;
83 SelectableFilesWidget *m_filesWidget;
84 QString m_qtModules;
85 QString m_buildSystem;
86 };
87
FilesSelectionWizardPage(SimpleProjectWizardDialog * simpleProjectWizard)88 FilesSelectionWizardPage::FilesSelectionWizardPage(SimpleProjectWizardDialog *simpleProjectWizard)
89 : m_simpleProjectWizardDialog(simpleProjectWizard),
90 m_filesWidget(new SelectableFilesWidget(this))
91 {
92 auto layout = new QVBoxLayout(this);
93 {
94 auto hlayout = new QHBoxLayout;
95 hlayout->addWidget(new QLabel("Qt modules", this));
96 auto lineEdit = new QLineEdit("core gui widgets", this);
97 connect(lineEdit, &QLineEdit::editingFinished, this, [this, lineEdit]{
98 m_qtModules = lineEdit->text();
99 });
100 m_qtModules = lineEdit->text();
101 hlayout->addWidget(lineEdit);
102 layout->addLayout(hlayout);
103 }
104
105 {
106 auto hlayout = new QHBoxLayout;
107 hlayout->addWidget(new QLabel("Build system", this));
108 auto comboBox = new QComboBox(this);
109 connect(comboBox, &QComboBox::currentTextChanged, this, [this](const QString &bs){
110 m_buildSystem = bs;
111 });
112 comboBox->addItems(QStringList() << "qmake" << "cmake");
113 comboBox->setEditable(false);
114 comboBox->setCurrentText("qmake");
115 hlayout->addWidget(comboBox);
116 layout->addLayout(hlayout);
117 }
118
119 layout->addWidget(m_filesWidget);
120 m_filesWidget->setBaseDirEditable(false);
121 m_filesWidget->enableFilterHistoryCompletion
122 (ProjectExplorer::Constants::ADD_FILES_DIALOG_FILTER_HISTORY_KEY);
123 connect(m_filesWidget, &SelectableFilesWidget::selectedFilesChanged,
124 this, &FilesSelectionWizardPage::completeChanged);
125
126 setProperty(Utils::SHORT_TITLE_PROPERTY, tr("Files"));
127 }
128
129 class SimpleProjectWizardDialog : public BaseFileWizard
130 {
131 Q_OBJECT
132
133 public:
SimpleProjectWizardDialog(const BaseFileWizardFactory * factory,QWidget * parent)134 SimpleProjectWizardDialog(const BaseFileWizardFactory *factory, QWidget *parent)
135 : BaseFileWizard(factory, QVariantMap(), parent)
136 {
137 setWindowTitle(tr("Import Existing Project"));
138
139 m_firstPage = new FileWizardPage;
140 m_firstPage->setTitle(tr("Project Name and Location"));
141 m_firstPage->setFileNameLabel(tr("Project name:"));
142 m_firstPage->setPathLabel(tr("Location:"));
143 addPage(m_firstPage);
144
145 m_secondPage = new FilesSelectionWizardPage(this);
146 m_secondPage->setTitle(tr("File Selection"));
147 addPage(m_secondPage);
148 }
149
path() const150 QString path() const { return m_firstPage->path(); }
setPath(const QString & path)151 void setPath(const QString &path) { m_firstPage->setPath(path); }
selectedFiles() const152 FilePaths selectedFiles() const { return m_secondPage->selectedFiles(); }
selectedPaths() const153 FilePaths selectedPaths() const { return m_secondPage->selectedPaths(); }
qtModules() const154 QString qtModules() const { return m_secondPage->qtModules(); }
buildSystem() const155 QString buildSystem() const { return m_secondPage->buildSystem(); }
projectName() const156 QString projectName() const { return m_firstPage->fileName(); }
157
158 FileWizardPage *m_firstPage;
159 FilesSelectionWizardPage *m_secondPage;
160 };
161
initializePage()162 void FilesSelectionWizardPage::initializePage()
163 {
164 m_filesWidget->resetModel(FilePath::fromString(m_simpleProjectWizardDialog->path()),
165 FilePaths());
166 }
167
SimpleProjectWizard()168 SimpleProjectWizard::SimpleProjectWizard()
169 {
170 setSupportedProjectTypes({QmakeProjectManager::Constants::QMAKEPROJECT_ID,
171 CMakeProjectManager::Constants::CMAKE_PROJECT_ID});
172 setIcon(QIcon(QLatin1String(":/projectexplorer/images/importasproject.png")));
173 setDisplayName(tr("Import as qmake or cmake Project (Limited Functionality)"));
174 setId("Z.DummyProFile");
175 setDescription(tr("Imports existing projects that do not use qmake, CMake, Qbs, Meson, or Autotools.<p>"
176 "This creates a project file that allows you to use %1 as a code editor "
177 "and as a launcher for debugging and analyzing tools. "
178 "If you want to build the project, you might need to edit the generated project file.")
179 .arg(Core::Constants::IDE_DISPLAY_NAME));
180 setCategory(ProjectExplorer::Constants::IMPORT_WIZARD_CATEGORY);
181 setDisplayCategory(ProjectExplorer::Constants::IMPORT_WIZARD_CATEGORY_DISPLAY);
182 setFlags(IWizardFactory::PlatformIndependent);
183 }
184
create(QWidget * parent,const WizardDialogParameters & parameters) const185 BaseFileWizard *SimpleProjectWizard::create(QWidget *parent,
186 const WizardDialogParameters ¶meters) const
187 {
188 auto wizard = new SimpleProjectWizardDialog(this, parent);
189 wizard->setPath(parameters.defaultPath());
190
191 for (QWizardPage *p : wizard->extensionPages())
192 wizard->addPage(p);
193
194 return wizard;
195 }
196
generateQmakeFiles(const SimpleProjectWizardDialog * wizard,QString * errorMessage)197 GeneratedFiles generateQmakeFiles(const SimpleProjectWizardDialog *wizard,
198 QString *errorMessage)
199 {
200 Q_UNUSED(errorMessage)
201 const QString projectPath = wizard->path();
202 const QDir dir(projectPath);
203 const QString projectName = wizard->projectName();
204 const QString proFileName = QFileInfo(dir, projectName + ".pro").absoluteFilePath();
205 const QStringList paths = Utils::transform(wizard->selectedPaths(), &FilePath::toString);
206
207 MimeType headerType = Utils::mimeTypeForName("text/x-chdr");
208
209 QStringList nameFilters = headerType.globPatterns();
210
211 QString proIncludes = "INCLUDEPATH = \\\n";
212 for (const QString &path : paths) {
213 QFileInfo fileInfo(path);
214 QDir thisDir(fileInfo.absoluteFilePath());
215 if (!thisDir.entryList(nameFilters, QDir::Files).isEmpty()) {
216 QString relative = dir.relativeFilePath(path);
217 if (!relative.isEmpty())
218 proIncludes.append(" $$PWD/" + relative + " \\\n");
219 }
220 }
221
222 QString proSources = "SOURCES = \\\n";
223 QString proHeaders = "HEADERS = \\\n";
224
225 for (const FilePath &fileName : wizard->selectedFiles()) {
226 QString source = dir.relativeFilePath(fileName.toString());
227 MimeType mimeType = Utils::mimeTypeForFile(fileName.toFileInfo());
228 if (mimeType.matchesName("text/x-chdr") || mimeType.matchesName("text/x-c++hdr"))
229 proHeaders += " $$PWD/" + source + " \\\n";
230 else
231 proSources += " $$PWD/" + source + " \\\n";
232 }
233
234 proHeaders.chop(3);
235 proSources.chop(3);
236 proIncludes.chop(3);
237
238 GeneratedFile generatedProFile(proFileName);
239 generatedProFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute);
240 generatedProFile.setContents(
241 "# Created by and for " + QLatin1String(Core::Constants::IDE_DISPLAY_NAME)
242 + " This file was created for editing the project sources only.\n"
243 "# You may attempt to use it for building too, by modifying this file here.\n\n"
244 "#TARGET = " + projectName + "\n\n"
245 "QT = " + wizard->qtModules() + "\n\n"
246 + proHeaders + "\n\n"
247 + proSources + "\n\n"
248 + proIncludes + "\n\n"
249 "#DEFINES = \n\n"
250 );
251
252 return GeneratedFiles{generatedProFile};
253 }
254
generateCmakeFiles(const SimpleProjectWizardDialog * wizard,QString * errorMessage)255 GeneratedFiles generateCmakeFiles(const SimpleProjectWizardDialog *wizard,
256 QString *errorMessage)
257 {
258 Q_UNUSED(errorMessage)
259 const QString projectPath = wizard->path();
260 const QDir dir(projectPath);
261 const QString projectName = wizard->projectName();
262 const QString projectFileName = QFileInfo(dir, "CMakeLists.txt").absoluteFilePath();
263 const QStringList paths = Utils::transform(wizard->selectedPaths(), &FilePath::toString);
264
265 MimeType headerType = Utils::mimeTypeForName("text/x-chdr");
266
267 QStringList nameFilters = headerType.globPatterns();
268
269 QString includes = "include_directories(\n";
270 bool haveIncludes = false;
271 for (const QString &path : paths) {
272 QFileInfo fileInfo(path);
273 QDir thisDir(fileInfo.absoluteFilePath());
274 if (!thisDir.entryList(nameFilters, QDir::Files).isEmpty()) {
275 QString relative = dir.relativeFilePath(path);
276 if (!relative.isEmpty()) {
277 includes.append(" " + relative + "\n");
278 haveIncludes = true;
279 }
280 }
281 }
282 if (haveIncludes)
283 includes += ")";
284 else
285 includes.clear();
286
287 QString srcs = "set (SRCS\n";
288 for (const FilePath &fileName : wizard->selectedFiles())
289 srcs += " " + dir.relativeFilePath(fileName.toString()) + "\n";
290 srcs += ")\n";
291
292 QString components = "find_package(Qt5 COMPONENTS";
293 QString libs = "target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE";
294 bool haveQtModules = false;
295 for (QString c : wizard->qtModules().split(' ')) {
296 if (c.isEmpty())
297 continue;
298 c[0] = c[0].toUpper();
299 libs += " Qt5::" + c;
300 components += " " + c;
301 haveQtModules = true;
302 }
303 if (haveQtModules) {
304 libs += ")\n";
305 components += " REQUIRED)";
306 } else {
307 libs.clear();
308 components.clear();
309 }
310
311
312 GeneratedFile generatedProFile(projectFileName);
313 generatedProFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute);
314 generatedProFile.setContents(
315 "# Created by and for " + QLatin1String(Core::Constants::IDE_DISPLAY_NAME)
316 + " This file was created for editing the project sources only.\n"
317 "# You may attempt to use it for building too, by modifying this file here.\n\n"
318 "cmake_minimum_required(VERSION 3.5)\n"
319 "project("+ projectName +")\n\n"
320 "set(CMAKE_INCLUDE_CURRENT_DIR ON)\n"
321 "set(CMAKE_AUTOUIC ON)\n"
322 "set(CMAKE_AUTOMOC ON)\n"
323 "set(CMAKE_AUTORCC ON)\n"
324 "set(CMAKE_CXX_STANDARD 11)\n"
325 "set(CMAKE_CXX_STANDARD_REQUIRED ON)\n"
326 + components + "\n\n"
327 + includes + "\n\n"
328 + srcs + "\n\n"
329 "add_executable(${CMAKE_PROJECT_NAME} ${SRCS})\n\n"
330 + libs
331 );
332 return GeneratedFiles{generatedProFile};
333 }
334
generateFiles(const QWizard * w,QString * errorMessage) const335 GeneratedFiles SimpleProjectWizard::generateFiles(const QWizard *w,
336 QString *errorMessage) const
337 {
338 Q_UNUSED(errorMessage)
339
340 auto wizard = qobject_cast<const SimpleProjectWizardDialog *>(w);
341 if (wizard->buildSystem() == "qmake")
342 return generateQmakeFiles(wizard, errorMessage);
343 else if (wizard->buildSystem() == "cmake")
344 return generateCmakeFiles(wizard, errorMessage);
345
346 if (errorMessage)
347 *errorMessage = tr("Unknown build system \"%1\"").arg(wizard->buildSystem());
348 return {};
349 }
350
postGenerateFiles(const QWizard * w,const GeneratedFiles & l,QString * errorMessage) const351 bool SimpleProjectWizard::postGenerateFiles(const QWizard *w, const GeneratedFiles &l,
352 QString *errorMessage) const
353 {
354 Q_UNUSED(w)
355 return CustomProjectWizard::postGenerateOpen(l, errorMessage);
356 }
357
358 } // namespace Internal
359 } // namespace GenericProjectManager
360
361 #include "simpleprojectwizard.moc"
362