1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
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
16 ** GNU Lesser General Public License Usage
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.
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.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "formtemplatewizardpage.h"
35 #include "formeditorw.h"
36 #include "designerconstants.h"
38 #include "qt_private/abstractnewformwidget_p.h"
40 #include <QtCore/QDebug>
41 #include <QtCore/QXmlStreamReader>
42 #include <QtCore/QXmlStreamAttributes>
43 #include <QtCore/QByteArray>
44 #include <QtCore/QBuffer>
46 #include <QtGui/QVBoxLayout>
47 #include <QtGui/QMessageBox>
48 #include <QtGui/QAbstractButton>
51 # include <QtXmlPatterns/QXmlQuery>
53 # include <QtXml/QDomDocument>
59 // ----------------- FormTemplateWizardPage
61 FormTemplateWizardPage::FormTemplateWizardPage(QWidget * parent) :
63 m_newFormWidget(QDesignerNewFormWidgetInterface::createNewFormWidget(FormEditorW::instance()->designerEditor())),
64 m_templateSelected(m_newFormWidget->hasCurrentTemplate())
66 setTitle(tr("Choose a Form Template"));
67 QVBoxLayout *layout = new QVBoxLayout;
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);
78 bool FormTemplateWizardPage::isComplete() const
80 return m_templateSelected;
83 void FormTemplateWizardPage::slotCurrentTemplateChanged(bool templateSelected)
85 if (m_templateSelected == templateSelected)
87 m_templateSelected = templateSelected;
88 emit completeChanged();
91 bool FormTemplateWizardPage::validatePage()
94 m_templateContents = m_newFormWidget->currentTemplate(&errorMessage);
95 if (m_templateContents.isEmpty()) {
96 QMessageBox::critical(this, tr("%1 - Error").arg(title()), errorMessage);
102 QString FormTemplateWizardPage::stripNamespaces(const QString &className)
104 QString rc = className;
105 const int namespaceIndex = rc.lastIndexOf(QLatin1String("::"));
106 if (namespaceIndex != -1)
107 rc.remove(0, namespaceIndex + 2);
111 bool FormTemplateWizardPage::getUIXmlData(const QString &uiXml,
112 QString *formBaseClass,
113 QString *uiClassName)
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();
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();
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
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"
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"
149 "<!-- heavy wizardry to copy nodes and attributes (emulating the default rules) -->\n"
150 "<xsl:template match=\"@*|node()\">\n"
152 " <xsl:apply-templates select=\"@*|node()\"/>\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"
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"
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"
176 "<xsl:template match=\"sender[.='Dialog']\">\n"
177 " <xsl:element name=\"sender\">\n"
178 " <xsl:text>%1</xsl:text>\n"
181 "</xsl:stylesheet>\n";
183 QString FormTemplateWizardPage::changeUiClassName(const QString &uiXml, const QString &newUiClassName)
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);
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);
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());
207 outputBuffer.close();
208 return QString::fromUtf8(outputBuffer.data());
212 // Change the contents of a DOM element to a new value if it matches
214 template <class Predicate>
215 bool changeDomElementContents(const QDomElement &element,
217 const QString &newValue,
218 QString *ptrToOldValue = 0)
220 // Find text in "<element>text</element>"
221 const QDomNodeList children = element.childNodes();
222 if (children.size() != 1)
224 const QDomNode first = children.at(0);
225 if (first.nodeType() != QDomNode::TextNode)
227 QDomCharacterData data = first.toCharacterData();
228 const QString oldValue = data.data();
232 *ptrToOldValue = oldValue;
233 data.setData(newValue);
240 bool truePredicate(const QString &) { return true; }
242 // Predicate that matches a string value
243 class MatchPredicate {
245 MatchPredicate(const QString &m) : m_match(m) {}
246 bool operator()(const QString &s) const { return s == m_match; }
248 const QString m_match;
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)
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();
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);
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
284 QString FormTemplateWizardPage::changeUiClassName(const QString &uiXml, const QString &newUiClassName)
286 if (Designer::Constants::Internal::debug)
287 qDebug() << '>' << Q_FUNC_INFO << newUiClassName;
289 if (!domUi.setContent(uiXml)) {
290 qWarning("Failed to parse:\n%s", uiXml.toUtf8().constData());
294 bool firstWidgetElementFound = false;
295 QString oldClassName;
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());
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);
322 // Replace <sender>, <receiver> tags of dialogs.
323 if (name == connectionsTag)
324 changeDomConnectionList(element, oldClassName, newUiClassName);
329 const QString rc = domUi.toString();
330 if (Designer::Constants::Internal::debug > 1)
331 qDebug() << '<' << Q_FUNC_INFO << newUiClassName << rc;
336 } // namespace Internal
337 } // namespace Designer