OSDN Git Service

3f06b927e7a7b9ae576bc6a7b6e79036854722f3
[qt-creator-jp/qt-creator-jp.git] / src / plugins / designer / formtemplatewizardpage.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 "formtemplatewizardpage.h"
35 #include "formeditorw.h"
36 #include "designerconstants.h"
37
38 #include "qt_private/abstractnewformwidget_p.h"
39
40 #include <QtCore/QDebug>
41 #include <QtCore/QXmlStreamReader>
42 #include <QtCore/QXmlStreamAttributes>
43 #include <QtCore/QByteArray>
44 #include <QtCore/QBuffer>
45
46 #include <QtGui/QVBoxLayout>
47 #include <QtGui/QMessageBox>
48 #include <QtGui/QAbstractButton>
49
50 #ifdef USE_XSLT
51 #    include <QtXmlPatterns/QXmlQuery>
52 #else
53 #    include <QtXml/QDomDocument>
54 #endif
55
56 namespace Designer {
57 namespace Internal {
58
59 // ----------------- FormTemplateWizardPage
60
61 FormTemplateWizardPage::FormTemplateWizardPage(QWidget * parent) :
62     QWizardPage(parent),
63     m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(FormEditorW::instance()->designerEditor())),
64     m_templateSelected(m_newFormWidget->hasCurrentTemplate())
65 {
66     setTitle(tr("Choose a Form Template"));
67     QVBoxLayout *layout = new QVBoxLayout;
68
69     connect(m_newFormWidget, SIGNAL(currentTemplateChanged(bool)),
70             this, SLOT(slotCurrentTemplateChanged(bool)));
71     connect(m_newFormWidget, SIGNAL(templateActivated()),
72             this, SIGNAL(templateActivated()));
73     layout->addWidget(m_newFormWidget);
74
75     setLayout(layout);
76 }
77
78 bool FormTemplateWizardPage::isComplete() const
79 {
80     return m_templateSelected;
81 }
82
83 void FormTemplateWizardPage::slotCurrentTemplateChanged(bool templateSelected)
84 {
85     if (m_templateSelected == templateSelected)
86         return;
87     m_templateSelected = templateSelected;
88     emit completeChanged();
89 }
90
91 bool FormTemplateWizardPage::validatePage()
92 {
93     QString errorMessage;
94     m_templateContents = m_newFormWidget->currentTemplate(&errorMessage);
95     if (m_templateContents.isEmpty()) {
96         QMessageBox::critical(this, tr("%1 - Error").arg(title()), errorMessage);
97         return false;
98     }
99     return true;
100 }
101
102 QString FormTemplateWizardPage::stripNamespaces(const QString &className)
103 {
104     QString rc = className;
105     const int namespaceIndex = rc.lastIndexOf(QLatin1String("::"));
106     if (namespaceIndex != -1)
107         rc.remove(0, namespaceIndex + 2);
108     return rc;
109 }
110
111 bool FormTemplateWizardPage::getUIXmlData(const QString &uiXml,
112                                               QString *formBaseClass,
113                                               QString *uiClassName)
114 {
115     // Parse UI xml to determine
116     // 1) The ui class name from "<class>Designer::Internal::FormClassWizardPage</class>"
117     // 2) the base class from: widget class="QWizardPage"...
118     QXmlStreamReader reader(uiXml);
119     while (!reader.atEnd()) {
120         if (reader.readNext() == QXmlStreamReader::StartElement) {
121             if (reader.name() == QLatin1String("class")) {
122                 *uiClassName = reader.readElementText();
123             } else {
124                 if (reader.name() == QLatin1String("widget")) {
125                     const QXmlStreamAttributes attrs = reader.attributes();
126                     *formBaseClass = reader.attributes().value(QLatin1String("class")).toString();
127                     return !uiClassName->isEmpty() && !formBaseClass->isEmpty();
128                 }
129             }
130         }
131     }
132     return false;
133 }
134
135 #ifdef USE_XSLT
136
137 // Change the UI class name in UI xml: This occurs several times, as contents
138 // of the <class> element, as name of the first <widget> element, and possibly
139 // in the signal/slot connections
140
141 static const char *classNameChangingSheetFormatC =
142 "<?xml version=\"1.0\"?>\n"
143 "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"2.0\">\n"
144 "<xsl:output method=\"xml\" indent=\"yes\" encoding=\"UTF-8\" />\n"
145 "\n"
146 "<!-- Grab old class name to be replaced by evaluating <ui><class> -->\n"
147 "<xsl:param name=\"oldClassName\"><xsl:value-of select=\"/ui/class\"/></xsl:param>\n"
148 "\n"
149 "<!-- heavy wizardry to copy nodes and attributes (emulating the default  rules) -->\n"
150 "<xsl:template match=\"@*|node()\">\n"
151 "   <xsl:copy>\n"
152 "        <xsl:apply-templates select=\"@*|node()\"/>\n"
153 "   </xsl:copy>\n"
154 "</xsl:template>\n"
155 "\n"
156 "<!-- Change <ui><class> tag -->\n"
157 "<xsl:template match=\"class\">\n"
158 "    <xsl:element name=\"class\">\n"
159 "    <xsl:text>%1</xsl:text>\n"
160 "    </xsl:element>\n"
161 "</xsl:template>\n"
162 "\n"
163 "<!-- Change first <widget> tag -->\n"
164 "<xsl:template match=\"/ui/widget/@name\">\n"
165 "<xsl:attribute name=\"name\">\n"
166 "<xsl:text>%1</xsl:text>\n"
167 "</xsl:attribute>\n"
168 "</xsl:template>\n"
169 "\n"
170 "<!-- Change <sender>/<receiver> elements (for pre-wired QDialog signals) -->\n"
171 "<xsl:template match=\"receiver[.='Dialog']\">\n"
172 "    <xsl:element name=\"receiver\">\n"
173 "        <xsl:text>%1</xsl:text>\n"
174 "    </xsl:element>\n"
175 "</xsl:template>\n"
176 "<xsl:template match=\"sender[.='Dialog']\">\n"
177 "    <xsl:element name=\"sender\">\n"
178 "        <xsl:text>%1</xsl:text>\n"
179 "    </xsl:element>\n"
180 "</xsl:template>\n"
181 "</xsl:stylesheet>\n";
182
183 QString FormTemplateWizardPage::changeUiClassName(const QString &uiXml, const QString &newUiClassName)
184 {
185     // Prepare I/O: Sheet
186     const QString xsltSheet = QString::fromLatin1(classNameChangingSheetFormatC).arg(newUiClassName);
187     QByteArray xsltSheetBA = xsltSheet.toUtf8();
188     QBuffer xsltSheetBuffer(&xsltSheetBA);
189     xsltSheetBuffer.open(QIODevice::ReadOnly);
190     // Prepare I/O: Xml
191     QByteArray xmlBA = uiXml.toUtf8();
192     QBuffer  xmlBuffer(&xmlBA);
193     xmlBuffer.open(QIODevice::ReadOnly);
194     // Prepare I/O: output
195     QBuffer outputBuffer;
196     outputBuffer.open(QIODevice::WriteOnly);
197
198     // Run query
199     QXmlQuery query(QXmlQuery::XSLT20);
200     query.setFocus(&xmlBuffer);
201     query.setQuery(&xsltSheetBuffer);
202     if (!query.evaluateTo(&outputBuffer)) {
203         qWarning("Unable to change the ui class name in a form template.\n%s\nUsing:\n%s\n",
204                  xmlBA.constData(), xsltSheetBA.constData());
205         return uiXml;
206     }
207     outputBuffer.close();
208     return QString::fromUtf8(outputBuffer.data());
209 }
210 #else
211
212 // Change the contents of a DOM element to a new value if it matches
213 // a predicate
214 template <class Predicate>
215 bool changeDomElementContents(const QDomElement &element,
216                            Predicate p,
217                            const QString &newValue,
218                            QString *ptrToOldValue = 0)
219 {
220     // Find text in "<element>text</element>"
221     const QDomNodeList children = element.childNodes();
222     if (children.size() != 1)
223         return false;
224     const QDomNode first = children.at(0);
225     if (first.nodeType() != QDomNode::TextNode)
226         return false;
227     QDomCharacterData data = first.toCharacterData();
228     const QString oldValue = data.data();
229
230     if (p(oldValue)) {
231         if (ptrToOldValue)
232             *ptrToOldValue = oldValue;
233         data.setData(newValue);
234         return true;
235     }
236     return false;
237 }
238
239 namespace {
240     bool truePredicate(const QString &) { return true; }
241
242     // Predicate that matches a string value
243     class MatchPredicate {
244     public:
245         MatchPredicate(const QString &m) : m_match(m) {}
246         bool operator()(const QString &s) const { return s == m_match; }
247     private:
248         const QString m_match;
249     };
250
251     // Change <sender> and <receiver> in a Dom UI <connections> list
252     // if they match the class name passed on
253     void changeDomConnectionList(const QDomElement &connectionsNode,
254                                  const QString &oldClassName,
255                                  const QString &newClassName)
256     {
257         const MatchPredicate oldClassPredicate(oldClassName);
258         const QString senderTag = QLatin1String("sender");
259         const QString receiverTag = QLatin1String("receiver");
260         const QDomNodeList connections = connectionsNode.childNodes();
261         const int connectionsCount =  connections.size();
262         // Loop <connection>
263         for (int c = 0; c < connectionsCount; c++) {
264             const QDomNodeList connectionElements = connections.at(c).childNodes();
265             const int connectionElementCount = connectionElements.count();
266             // Loop <sender>, <receiver>, <signal>, <slot>
267             for (int ce = 0; ce < connectionElementCount; ce++) {
268                 const QDomNode connectionElementNode = connectionElements.at(ce);
269                 if (connectionElementNode.isElement()) {
270                     const QDomElement connectionElement = connectionElementNode.toElement();
271                     const QString tagName =  connectionElement.tagName();
272                     if (tagName == senderTag || tagName == receiverTag)
273                         changeDomElementContents(connectionElement, oldClassPredicate, newClassName);
274                 }
275             }
276         }
277     }
278 }
279
280 // Change the UI class name in UI xml: This occurs several times, as contents
281 // of the <class> element, as name of the first <widget> element, and possibly
282 // in the signal/slot connections
283
284 QString FormTemplateWizardPage::changeUiClassName(const QString &uiXml, const QString &newUiClassName)
285 {
286     if (Designer::Constants::Internal::debug)
287         qDebug() << '>' << Q_FUNC_INFO << newUiClassName;
288     QDomDocument domUi;
289     if (!domUi.setContent(uiXml)) {
290         qWarning("Failed to parse:\n%s", uiXml.toUtf8().constData());
291         return uiXml;
292     }
293
294     bool firstWidgetElementFound = false;
295     QString oldClassName;
296
297     // Loop first level children. First child is <ui>
298     const QDomNodeList children = domUi.firstChildElement().childNodes();
299     const QString classTag = QLatin1String("class");
300     const QString widgetTag = QLatin1String("widget");
301     const QString connectionsTag = QLatin1String("connections");
302     const int count = children.size();
303     for (int i = 0; i < count; i++) {
304         const QDomNode node = children.at(i);
305         if (node.isElement()) {
306             // Replace <class> element text
307             QDomElement element = node.toElement();
308             const QString name = element.tagName();
309             if (name == classTag) {
310                 if (!changeDomElementContents(element, truePredicate, newUiClassName, &oldClassName)) {
311                     qWarning("Unable to change the <class> element:\n%s", uiXml.toUtf8().constData());
312                     return uiXml;
313                 }
314             } else {
315                 // Replace first <widget> element name attribute
316                 if (!firstWidgetElementFound && name == widgetTag) {
317                     firstWidgetElementFound = true;
318                     const QString nameAttribute = QLatin1String("name");
319                     if (element.hasAttribute(nameAttribute))
320                         element.setAttribute(nameAttribute, newUiClassName);
321                 } else {
322                     // Replace <sender>, <receiver> tags of dialogs.
323                     if (name == connectionsTag)
324                         changeDomConnectionList(element, oldClassName, newUiClassName);
325                 }
326             }
327         }
328     }
329     const QString rc = domUi.toString();
330     if (Designer::Constants::Internal::debug > 1)
331         qDebug() << '<' << Q_FUNC_INFO << newUiClassName << rc;
332     return rc;
333 }
334 #endif // USE_XSLT
335
336 } // namespace Internal
337 } // namespace Designer