OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qt4projectmanager / customwidgetwizard / plugingenerator.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "plugingenerator.h"
35 #include "pluginoptions.h"
36
37 #include <coreplugin/basefilewizard.h>
38
39 #include <cpptools/abstracteditorsupport.h>
40
41 #include <QtCore/QFileInfo>
42 #include <QtCore/QDir>
43 #include <QtCore/QSet>
44
45 static QString headerGuard(const QString &header)
46 {
47     return header.toUpper().replace(QRegExp(QLatin1String("[^A-Z0-9]+")), QLatin1String("_"));
48 }
49
50 namespace Qt4ProjectManager {
51 namespace Internal {
52
53 struct ProjectContents {
54     QString tmpl;
55     QString library;
56     QString headers;
57     QString sources;
58 };
59
60 // Create a binary icon file
61 static inline Core::GeneratedFile  generateIconFile(const QString &source, const QString &target, QString *errorMessage)
62 {
63     // Read out source
64     QFile iconFile(source);
65     if (!iconFile.open(QIODevice::ReadOnly)) {
66         *errorMessage = PluginGenerator::tr("Cannot open icon file %1.").arg(source);
67         return Core::GeneratedFile();
68     }
69     const QByteArray iconData = iconFile.readAll();
70     Core::GeneratedFile rc(target);
71     rc.setBinaryContents(iconData);
72     rc.setBinary(true);
73     return rc;
74 }
75
76 QList<Core::GeneratedFile>  PluginGenerator::generatePlugin(const GenerationParameters& p, const PluginOptions &options,
77                                                             QString *errorMessage)
78 {
79     const QChar slash = QLatin1Char('/');
80     const QChar blank = QLatin1Char(' ');
81     QList<Core::GeneratedFile> rc;
82
83     QString baseDir = p.path;
84     baseDir += slash;
85     baseDir += p.fileName;
86     const QString slashLessBaseDir = baseDir;
87     baseDir += slash;
88
89     QSet<QString> widgetLibraries;
90     QSet<QString> widgetProjects;
91     QMap<QString,ProjectContents> widgetProjectContents;
92     QString pluginIncludes;
93     QString pluginAdditions;
94     QString pluginHeaders;
95     QString pluginSources;
96     QSet<QString> pluginIcons;
97
98     SubstitutionMap sm;
99
100     // First create the widget wrappers (plugins) and - if requested - skeletons
101     // for the widgets.
102     const int widgetCount = options.widgetOptions.size();
103     for (int i = 0; i < widgetCount; i++) {
104         const PluginOptions::WidgetOptions &wo = options.widgetOptions.at(i);
105         sm.clear();
106         sm.insert(QLatin1String("SINGLE_INCLUDE_GUARD"), headerGuard(wo.pluginHeaderFile));
107         sm.insert(QLatin1String("PLUGIN_CLASS"), wo.pluginClassName);
108         const QString pluginHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.h"), sm, errorMessage);
109         if (pluginHeaderContents.isEmpty())
110             return QList<Core::GeneratedFile>();
111         Core::GeneratedFile pluginHeader(baseDir + wo.pluginHeaderFile);
112         pluginHeader.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.pluginHeaderFile, wo.pluginClassName)
113                                  + pluginHeaderContents);
114         rc.push_back(pluginHeader);
115
116         sm.remove(QLatin1String("SINGLE_INCLUDE_GUARD"));
117         sm.insert(QLatin1String("PLUGIN_HEADER"), wo.pluginHeaderFile);
118         sm.insert(QLatin1String("WIDGET_CLASS"), wo.widgetClassName);
119         sm.insert(QLatin1String("WIDGET_HEADER"), wo.widgetHeaderFile);
120         sm.insert(QLatin1String("WIDGET_GROUP"), wo.group);
121         QString iconResource;
122         if (!wo.iconFile.isEmpty()) {
123             iconResource = QLatin1String("QLatin1String(\":/");
124             iconResource += QFileInfo(wo.iconFile).fileName();
125             iconResource += QLatin1String("\")");
126         }
127         sm.insert(QLatin1String("WIDGET_ICON"),iconResource);
128         sm.insert(QLatin1String("WIDGET_TOOLTIP"), cStringQuote(wo.toolTip));
129         sm.insert(QLatin1String("WIDGET_WHATSTHIS"), cStringQuote(wo.whatsThis));
130         sm.insert(QLatin1String("WIDGET_ISCONTAINER"), wo.isContainer ? QLatin1String("true") : QLatin1String("false"));
131         sm.insert(QLatin1String("WIDGET_DOMXML"), cStringQuote(wo.domXml));
132         sm.insert(QLatin1String("SINGLE_PLUGIN_EXPORT"),
133             options.widgetOptions.count() == 1 ?
134                 QLatin1String("\nQ_EXPORT_PLUGIN2(") +
135                     options.pluginName +
136                     QLatin1String(", ") +
137                     wo.pluginClassName +
138                     QLatin1Char(')') :
139                 QString());
140         const QString pluginSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.cpp"), sm, errorMessage);
141         if (pluginSourceContents.isEmpty())
142             return QList<Core::GeneratedFile>();
143         Core::GeneratedFile pluginSource(baseDir + wo.pluginSourceFile);
144         pluginSource.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.pluginSourceFile, wo.pluginClassName)
145                                  + pluginSourceContents);
146         if (i == 0 && widgetCount == 1) // Open first widget unless collection
147             pluginSource.setAttributes(Core::GeneratedFile::OpenEditorAttribute);
148         rc.push_back(pluginSource);
149
150         if (wo.sourceType == PluginOptions::WidgetOptions::LinkLibrary)
151             widgetLibraries.insert(QLatin1String("-l") + wo.widgetLibrary);
152         else
153             widgetProjects.insert(QLatin1String("include(") + wo.widgetProjectFile + QLatin1String(")"));
154         pluginIncludes += QLatin1String("#include \"") + wo.pluginHeaderFile + QLatin1String("\"\n");
155         pluginAdditions +=
156             QLatin1String("    m_widgets.append(new ") + wo.pluginClassName + QLatin1String("(this));\n");
157         pluginHeaders += QLatin1Char(' ') + wo.pluginHeaderFile;
158         pluginSources += QLatin1Char(' ') + wo.pluginSourceFile;
159         if (!wo.iconFile.isEmpty())
160             pluginIcons.insert(wo.iconFile);
161
162         if (wo.createSkeleton) {
163             ProjectContents &pc = widgetProjectContents[wo.widgetProjectFile];
164             if (pc.headers.isEmpty()) {
165                 if (wo.sourceType == PluginOptions::WidgetOptions::LinkLibrary) {
166                     pc.library = wo.widgetLibrary;
167                     pc.tmpl = p.templatePath + QLatin1String("/tpl_widget_lib.pro");
168                 } else {
169                     pc.tmpl = p.templatePath + QLatin1String("/tpl_widget_include.pri");
170                 }
171                 widgetProjectContents.insert(wo.widgetProjectFile, pc);
172             } else {
173                 if (pc.library != wo.widgetLibrary) {
174                     *errorMessage = tr("Creating multiple widget libraries (%1, %2) in one project (%3) is not supported.")
175                         .arg(pc.library, wo.widgetLibrary, wo.widgetProjectFile);
176                     return QList<Core::GeneratedFile>();
177                 }
178             }
179             pc.headers += blank + wo.widgetHeaderFile;
180             pc.sources += blank + wo.widgetSourceFile;
181
182             sm.clear();
183             sm.insert(QLatin1String("WIDGET_INCLUDE_GUARD"), headerGuard(wo.widgetHeaderFile));
184             sm.insert(QLatin1String("WIDGET_BASE_CLASS"), wo.widgetBaseClassName);
185             sm.insert(QLatin1String("WIDGET_CLASS"), wo.widgetClassName);
186             const QString widgetHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_widget.h"), sm, errorMessage);
187             if (widgetHeaderContents.isEmpty())
188                 return QList<Core::GeneratedFile>();
189             Core::GeneratedFile widgetHeader(baseDir + wo.widgetHeaderFile);
190             widgetHeader.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.widgetHeaderFile, wo.widgetClassName)
191                                      + widgetHeaderContents);
192             rc.push_back(widgetHeader);
193
194             sm.remove(QLatin1String("WIDGET_INCLUDE_GUARD"));
195             sm.insert(QLatin1String("WIDGET_HEADER"), wo.widgetHeaderFile);
196             const QString widgetSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_widget.cpp"), sm, errorMessage);
197             if (widgetSourceContents.isEmpty())
198                 return QList<Core::GeneratedFile>();
199             Core::GeneratedFile widgetSource(baseDir + wo.widgetSourceFile);
200             widgetSource.setContents(CppTools::AbstractEditorSupport::licenseTemplate(wo.widgetSourceFile, wo.widgetClassName)
201                                      + widgetSourceContents);
202             rc.push_back(widgetSource);
203         }
204     }
205
206     // Then create the project files for the widget skeletons.
207     // These might create widgetLibraries or be included into the plugin's project.
208     QMap<QString,ProjectContents>::const_iterator it = widgetProjectContents.constBegin();
209     const QMap<QString,ProjectContents>::const_iterator end = widgetProjectContents.constEnd();
210     for (; it != end; ++it) {
211         const ProjectContents &pc = it.value();
212         sm.clear();
213         sm.insert(QLatin1String("WIDGET_HEADERS"), pc.headers);
214         sm.insert(QLatin1String("WIDGET_SOURCES"), pc.sources);
215         if (!pc.library.isEmpty())
216             sm.insert(QLatin1String("WIDGET_LIBRARY"), pc.library);
217         const QString widgetPriContents = processTemplate(pc.tmpl, sm, errorMessage);
218         if (widgetPriContents.isEmpty())
219             return QList<Core::GeneratedFile>();
220         Core::GeneratedFile widgetPri(baseDir + it.key());
221         widgetPri.setContents(widgetPriContents);
222         rc.push_back(widgetPri);
223     }
224
225     // Create the sources for the collection if necessary.
226     if (widgetCount > 1) {
227         sm.clear();
228         sm.insert(QLatin1String("COLLECTION_INCLUDE_GUARD"), headerGuard(options.collectionHeaderFile));
229         sm.insert(QLatin1String("COLLECTION_PLUGIN_CLASS"), options.collectionClassName);
230         const QString collectionHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.h"), sm, errorMessage);
231         if (collectionHeaderContents.isEmpty())
232             return QList<Core::GeneratedFile>();
233         Core::GeneratedFile collectionHeader(baseDir + options.collectionHeaderFile);
234         collectionHeader.setContents(CppTools::AbstractEditorSupport::licenseTemplate(options.collectionHeaderFile, options.collectionClassName)
235                                      + collectionHeaderContents);
236         rc.push_back(collectionHeader);
237
238         sm.remove(QLatin1String("COLLECTION_INCLUDE_GUARD"));
239         sm.insert(QLatin1String("PLUGIN_INCLUDES"),
240             pluginIncludes +
241             QLatin1String("#include \"") +
242                 options.collectionHeaderFile +
243                 QLatin1String("\""));
244         sm.insert(QLatin1String("PLUGIN_ADDITIONS"), pluginAdditions);
245         sm.insert(QLatin1String("COLLECTION_PLUGIN_EXPORT"),
246             QLatin1String("Q_EXPORT_PLUGIN2(") +
247                 options.pluginName +
248                 QLatin1String(", ") +
249                 options.collectionClassName +
250                 QLatin1Char(')'));
251         const QString collectionSourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.cpp"), sm, errorMessage);
252         if (collectionSourceFileContents.isEmpty())
253             return QList<Core::GeneratedFile>();
254         Core::GeneratedFile collectionSource(baseDir + options.collectionSourceFile);
255         collectionSource.setContents(CppTools::AbstractEditorSupport::licenseTemplate(options.collectionSourceFile, options.collectionClassName)
256                                      + collectionSourceFileContents);
257         collectionSource.setAttributes(Core::GeneratedFile::OpenEditorAttribute);
258         rc.push_back(collectionSource);
259
260         pluginHeaders += blank + options.collectionHeaderFile;
261         pluginSources += blank + options.collectionSourceFile;
262     }
263
264     // Copy icons that are not in the plugin source base directory yet (that is,
265     // probably all), add them to the resource file
266     QString iconFiles;
267     foreach (QString icon, pluginIcons) {
268         const QFileInfo qfi(icon);
269         if (qfi.dir() != slashLessBaseDir) {
270             const QString newIcon = baseDir + qfi.fileName();
271             const Core::GeneratedFile iconFile = generateIconFile(icon, newIcon, errorMessage);
272             if (iconFile.path().isEmpty())
273                 return QList<Core::GeneratedFile>();
274             rc.push_back(iconFile);
275             icon = qfi.fileName();
276         }
277         iconFiles += QLatin1String("        <file>") + icon + QLatin1String("</file>\n");
278     }
279     // Create the resource file with the icons.
280     sm.clear();
281     sm.insert(QLatin1String("ICON_FILES"), iconFiles);
282     const QString resourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_resources.qrc"), sm, errorMessage);
283     if (resourceFileContents.isEmpty())
284         return QList<Core::GeneratedFile>();
285     Core::GeneratedFile resourceFile(baseDir + options.resourceFile);
286     resourceFile.setContents(resourceFileContents);
287     rc.push_back(resourceFile);
288
289     // Finally create the project for the plugin itself.
290     sm.clear();
291     sm.insert(QLatin1String("PLUGIN_NAME"), options.pluginName);
292     sm.insert(QLatin1String("PLUGIN_HEADERS"), pluginHeaders);
293     sm.insert(QLatin1String("PLUGIN_SOURCES"), pluginSources);
294     sm.insert(QLatin1String("PLUGIN_RESOURCES"), options.resourceFile);
295     sm.insert(QLatin1String("WIDGET_LIBS"), QStringList(widgetLibraries.toList()).join(QString(blank)));
296     sm.insert(QLatin1String("INCLUSIONS"), QStringList(widgetProjects.toList()).join(QLatin1String("\n")));
297     const QString proFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_plugin.pro"), sm, errorMessage);
298     if (proFileContents.isEmpty())
299         return QList<Core::GeneratedFile>();
300     Core::GeneratedFile proFile(baseDir + p.fileName + QLatin1String(".pro"));
301     proFile.setContents(proFileContents);
302     proFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute);
303     rc.push_back(proFile);
304     return rc;
305 }
306
307 QString PluginGenerator::processTemplate(const QString &tmpl,
308                                          const SubstitutionMap &substMap,
309                                          QString *errorMessage)
310 {
311     QFile tpl(tmpl);
312     if (!tpl.open(QIODevice::ReadOnly|QIODevice::Text)) {
313         *errorMessage = tr("Cannot open %1: %2").arg(tmpl, tpl.errorString());
314         return QString();
315     }
316
317     QString cont = QString::fromUtf8(tpl.readAll());
318     const QChar atChar = QLatin1Char('@');
319     int offset = 0;
320     for (;;) {
321         const int start = cont.indexOf(atChar, offset);
322         if (start < 0)
323             break;
324         const int end = cont.indexOf(atChar, start + 1);
325         Q_ASSERT(end);
326         const QString keyword = cont.mid(start + 1, end - start - 1);
327         const QString replacement = substMap.value(keyword);
328         cont.replace(start, end - start + 1, replacement);
329         offset = start + replacement.length();
330     }
331     return cont;
332 }
333
334 QString PluginGenerator::cStringQuote(QString s)
335 {
336     s.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
337     s.replace(QLatin1Char('"'), QLatin1String("\\\""));
338     s.replace(QLatin1Char('\t'), QLatin1String("\\t"));
339     s.replace(QLatin1Char('\n'), QLatin1String("\\n"));
340     return s;
341 }
342
343 }
344 }