1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "newformwidget_p.h"
30 #include "ui_newformwidget.h"
31 #include "qdesigner_formbuilder_p.h"
32 #include "sheet_delegate_p.h"
33 #include "widgetdatabase_p.h"
34 #include "shared_settings_p.h"
35
36 #include <QtDesigner/abstractformeditor.h>
37 #include <QtDesigner/abstractformwindow.h>
38 #include <QtDesigner/qextensionmanager.h>
39 #include <QtDesigner/abstractlanguage.h>
40 #include <QtDesigner/abstractwidgetdatabase.h>
41
42 #include <QtCore/qdir.h>
43 #include <QtCore/qfile.h>
44 #include <QtCore/qfileinfo.h>
45 #include <QtCore/qdebug.h>
46 #include <QtCore/qbytearray.h>
47 #include <QtCore/qbuffer.h>
48 #include <QtCore/qdir.h>
49 #include <QtCore/qtextstream.h>
50
51 #include <QtWidgets/qapplication.h>
52 #include <QtWidgets/qdesktopwidget.h>
53 #include <QtWidgets/qheaderview.h>
54 #include <QtWidgets/qtreewidget.h>
55 #include <QtGui/qpainter.h>
56 #include <QtWidgets/qpushbutton.h>
57
58 QT_BEGIN_NAMESPACE
59
60 enum { profileComboIndexOffset = 1 };
61 enum { debugNewFormWidget = 0 };
62
63 enum NewForm_CustomRole {
64 // File name (templates from resources, paths)
65 TemplateNameRole = Qt::UserRole + 100,
66 // Class name (widgets from Widget data base)
67 ClassNameRole = Qt::UserRole + 101
68 };
69
70 static const char *newFormObjectNameC = "Form";
71
72 // Create a form name for an arbitrary class. If it is Qt, qtify it,
73 // else return "Form".
formName(const QString & className)74 static QString formName(const QString &className)
75 {
76 if (!className.startsWith(QLatin1Char('Q')))
77 return QLatin1String(newFormObjectNameC);
78 QString rc = className;
79 rc.remove(0, 1);
80 return rc;
81 }
82
83 namespace qdesigner_internal {
84
85 struct TemplateSize {
86 const char *name;
87 int width;
88 int height;
89 };
90
91 static const struct TemplateSize templateSizes[] =
92 {
93 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "Default size"), 0, 0 },
94 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA portrait (240x320)"), 240, 320 },
95 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA landscape (320x240)"), 320, 240 },
96 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA portrait (480x640)"), 480, 640 },
97 { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA landscape (640x480)"), 640, 480 }
98 };
99
100 /* -------------- NewForm dialog.
101 * Designer takes new form templates from:
102 * 1) Files located in directories specified in resources
103 * 2) Files located in directories specified as user templates
104 * 3) XML from container widgets deemed usable for form templates by the widget
105 * database
106 * 4) XML from custom container widgets deemed usable for form templates by the
107 * widget database
108 *
109 * The widget database provides helper functions to obtain lists of names
110 * and xml for 3,4.
111 *
112 * Fixed-size forms for embedded platforms are obtained as follows:
113 * 1) If the origin is a file:
114 * - Check if the file exists in the subdirectory "/<width>x<height>/" of
115 * the path (currently the case for the dialog box because the button box
116 * needs to be positioned)
117 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
118 * 2) If the origin is XML:
119 * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine.
120 *
121 * The tree widget item roles indicate which type of entry it is
122 * (TemplateNameRole = file name 1,2, ClassNameRole = class name 3,4)
123 */
124
NewFormWidget(QDesignerFormEditorInterface * core,QWidget * parentWidget)125 NewFormWidget::NewFormWidget(QDesignerFormEditorInterface *core, QWidget *parentWidget) :
126 QDesignerNewFormWidgetInterface(parentWidget),
127 m_core(core),
128 m_ui(new Ui::NewFormWidget),
129 m_currentItem(nullptr),
130 m_acceptedItem(nullptr)
131 {
132 m_ui->setupUi(this);
133 m_ui->treeWidget->setItemDelegate(new qdesigner_internal::SheetDelegate(m_ui->treeWidget, this));
134 m_ui->treeWidget->header()->hide();
135 m_ui->treeWidget->header()->setStretchLastSection(true);
136 m_ui->lblPreview->setBackgroundRole(QPalette::Base);
137 QDesignerSharedSettings settings(m_core);
138
139 QString uiExtension = QStringLiteral("ui");
140 QString templatePath = QStringLiteral(":/qt-project.org/designer/templates/forms");
141
142 QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core);
143 if (lang) {
144 templatePath = QStringLiteral(":/templates/forms");
145 uiExtension = lang->uiExtension();
146 }
147
148 // Resource templates
149 const QString formTemplate = settings.formTemplate();
150 QTreeWidgetItem *selectedItem = nullptr;
151 loadFrom(templatePath, true, uiExtension, formTemplate, selectedItem);
152 // Additional template paths
153 const QStringList formTemplatePaths = settings.formTemplatePaths();
154 const QStringList::const_iterator ftcend = formTemplatePaths.constEnd();
155 for (QStringList::const_iterator it = formTemplatePaths.constBegin(); it != ftcend; ++it)
156 loadFrom(*it, false, uiExtension, formTemplate, selectedItem);
157
158 // Widgets/custom widgets
159 if (!lang) {
160 //: New Form Dialog Categories
161 loadFrom(tr("Widgets"), qdesigner_internal::WidgetDataBase::formWidgetClasses(core), formTemplate, selectedItem);
162 loadFrom(tr("Custom Widgets"), qdesigner_internal::WidgetDataBase::customFormWidgetClasses(core), formTemplate, selectedItem);
163 }
164
165 // Still no selection - default to first item
166 if (selectedItem == nullptr && m_ui->treeWidget->topLevelItemCount() != 0) {
167 QTreeWidgetItem *firstTopLevel = m_ui->treeWidget->topLevelItem(0);
168 if (firstTopLevel->childCount() > 0)
169 selectedItem = firstTopLevel->child(0);
170 }
171
172 // Open parent, select and make visible
173 if (selectedItem) {
174 m_ui->treeWidget->setCurrentItem(selectedItem);
175 selectedItem->setSelected(true);
176 m_ui->treeWidget->scrollToItem(selectedItem->parent());
177 }
178 // Fill profile combo
179 m_deviceProfiles = settings.deviceProfiles();
180 m_ui->profileComboBox->addItem(tr("None"));
181 connect(m_ui->profileComboBox,
182 QOverload<int>::of(&QComboBox::currentIndexChanged),
183 this, &NewFormWidget::slotDeviceProfileIndexChanged);
184 if (m_deviceProfiles.isEmpty()) {
185 m_ui->profileComboBox->setEnabled(false);
186 } else {
187 for (const auto &deviceProfile : qAsConst(m_deviceProfiles))
188 m_ui->profileComboBox->addItem(deviceProfile.name());
189 const int ci = settings.currentDeviceProfileIndex();
190 if (ci >= 0)
191 m_ui->profileComboBox->setCurrentIndex(ci + profileComboIndexOffset);
192 }
193 // Fill size combo
194 for (const TemplateSize &t : templateSizes)
195 m_ui->sizeComboBox->addItem(tr(t.name), QSize(t.width, t.height));
196
197 setTemplateSize(settings.newFormSize());
198
199 if (debugNewFormWidget)
200 qDebug() << Q_FUNC_INFO << "Leaving";
201 }
202
~NewFormWidget()203 NewFormWidget::~NewFormWidget()
204 {
205 QDesignerSharedSettings settings (m_core);
206 settings.setNewFormSize(templateSize());
207 // Do not change previously stored item if dialog was rejected
208 if (m_acceptedItem)
209 settings.setFormTemplate(m_acceptedItem->text(0));
210 delete m_ui;
211 }
212
on_treeWidget_currentItemChanged(QTreeWidgetItem * current,QTreeWidgetItem *)213 void NewFormWidget::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
214 {
215 if (debugNewFormWidget)
216 qDebug() << Q_FUNC_INFO << current;
217 if (!current)
218 return;
219
220 if (!current->parent()) { // Top level item: Ensure expanded when browsing down
221 return;
222 }
223
224 m_currentItem = current;
225
226 emit currentTemplateChanged(showCurrentItemPixmap());
227 }
228
showCurrentItemPixmap()229 bool NewFormWidget::showCurrentItemPixmap()
230 {
231 bool rc = false;
232 if (m_currentItem) {
233 const QPixmap pixmap = formPreviewPixmap(m_currentItem);
234 if (pixmap.isNull()) {
235 m_ui->lblPreview->setText(tr("Error loading form"));
236 } else {
237 m_ui->lblPreview->setPixmap(pixmap);
238 rc = true;
239 }
240 }
241 return rc;
242 }
243
on_treeWidget_itemActivated(QTreeWidgetItem * item)244 void NewFormWidget::on_treeWidget_itemActivated(QTreeWidgetItem *item)
245 {
246 if (debugNewFormWidget)
247 qDebug() << Q_FUNC_INFO << item;
248
249 if (item->data(0, TemplateNameRole).isValid() || item->data(0, ClassNameRole).isValid())
250 emit templateActivated();
251 }
252
formPreviewPixmap(const QTreeWidgetItem * item)253 QPixmap NewFormWidget::formPreviewPixmap(const QTreeWidgetItem *item)
254 {
255 // Cache pixmaps per item/device profile
256 const ItemPixmapCacheKey cacheKey(item, profileComboIndex());
257 ItemPixmapCache::iterator it = m_itemPixmapCache.find(cacheKey);
258 if (it == m_itemPixmapCache.end()) {
259 // file or string?
260 const QVariant fileName = item->data(0, TemplateNameRole);
261 QPixmap rc;
262 if (fileName.type() == QVariant::String) {
263 rc = formPreviewPixmap(fileName.toString());
264 } else {
265 const QVariant classNameV = item->data(0, ClassNameRole);
266 Q_ASSERT(classNameV.type() == QVariant::String);
267 const QString className = classNameV.toString();
268 QByteArray data = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className)).toUtf8();
269 QBuffer buffer(&data);
270 buffer.open(QIODevice::ReadOnly);
271 rc = formPreviewPixmap(buffer);
272 }
273 if (rc.isNull()) // Retry invalid ones
274 return rc;
275 it = m_itemPixmapCache.insert(cacheKey, rc);
276 }
277 return it.value();
278 }
279
formPreviewPixmap(const QString & fileName) const280 QPixmap NewFormWidget::formPreviewPixmap(const QString &fileName) const
281 {
282 QFile f(fileName);
283 if (f.open(QFile::ReadOnly)) {
284 QFileInfo fi(fileName);
285 const QPixmap rc = formPreviewPixmap(f, fi.absolutePath());
286 f.close();
287 return rc;
288 }
289 qWarning() << "The file " << fileName << " could not be opened: " << f.errorString();
290 return QPixmap();
291 }
292
grabForm(QDesignerFormEditorInterface * core,QIODevice & file,const QString & workingDir,const qdesigner_internal::DeviceProfile & dp)293 QImage NewFormWidget::grabForm(QDesignerFormEditorInterface *core,
294 QIODevice &file,
295 const QString &workingDir,
296 const qdesigner_internal::DeviceProfile &dp)
297 {
298 qdesigner_internal::NewFormWidgetFormBuilder
299 formBuilder(core, dp);
300 if (!workingDir.isEmpty())
301 formBuilder.setWorkingDirectory(workingDir);
302
303 QWidget *widget = formBuilder.load(&file, nullptr);
304 if (!widget)
305 return QImage();
306
307 const QPixmap pixmap = widget->grab(QRect(0, 0, -1, -1));
308 widget->deleteLater();
309 return pixmap.toImage();
310 }
311
formPreviewPixmap(QIODevice & file,const QString & workingDir) const312 QPixmap NewFormWidget::formPreviewPixmap(QIODevice &file, const QString &workingDir) const
313 {
314 const QSizeF screenSize(QApplication::desktop()->screenGeometry(this).size());
315 const int previewSize = qRound(screenSize.width() / 7.5); // 256 on 1920px screens.
316 const int margin = previewSize / 32 - 1; // 7 on 1920px screens.
317 const int shadow = margin;
318
319 const QImage wimage = grabForm(m_core, file, workingDir, currentDeviceProfile());
320 if (wimage.isNull())
321 return QPixmap();
322 const qreal devicePixelRatio = wimage.devicePixelRatioF();
323 const QSize imageSize(previewSize - margin * 2, previewSize - margin * 2);
324 QImage image = wimage.scaled((QSizeF(imageSize) * devicePixelRatio).toSize(),
325 Qt::KeepAspectRatio, Qt::SmoothTransformation);
326 image.setDevicePixelRatio(devicePixelRatio);
327
328 QImage dest((QSizeF(previewSize, previewSize) * devicePixelRatio).toSize(),
329 QImage::Format_ARGB32_Premultiplied);
330 dest.setDevicePixelRatio(devicePixelRatio);
331 dest.fill(0);
332
333 QPainter p(&dest);
334 p.drawImage(margin, margin, image);
335
336 p.setPen(QPen(palette().brush(QPalette::WindowText), 0));
337
338 p.drawRect(QRectF(margin - 1, margin - 1, imageSize.width() + 1.5, imageSize.height() + 1.5));
339
340 const QColor dark(Qt::darkGray);
341 const QColor light(Qt::transparent);
342
343 // right shadow
344 {
345 const QRect rect(margin + imageSize.width() + 1, margin + shadow, shadow, imageSize.height() - shadow + 1);
346 QLinearGradient lg(rect.topLeft(), rect.topRight());
347 lg.setColorAt(0, dark);
348 lg.setColorAt(1, light);
349 p.fillRect(rect, lg);
350 }
351
352 // bottom shadow
353 {
354 const QRect rect(margin + shadow, margin + imageSize.height() + 1, imageSize.width() - shadow + 1, shadow);
355 QLinearGradient lg(rect.topLeft(), rect.bottomLeft());
356 lg.setColorAt(0, dark);
357 lg.setColorAt(1, light);
358 p.fillRect(rect, lg);
359 }
360
361 // bottom/right corner shadow
362 {
363 const QRect rect(margin + imageSize.width() + 1, margin + imageSize.height() + 1, shadow, shadow);
364 QRadialGradient g(rect.topLeft(), shadow - 1);
365 g.setColorAt(0, dark);
366 g.setColorAt(1, light);
367 p.fillRect(rect, g);
368 }
369
370 // top/right corner
371 {
372 const QRect rect(margin + imageSize.width() + 1, margin, shadow, shadow);
373 QRadialGradient g(rect.bottomLeft(), shadow - 1);
374 g.setColorAt(0, dark);
375 g.setColorAt(1, light);
376 p.fillRect(rect, g);
377 }
378
379 // bottom/left corner
380 {
381 const QRect rect(margin, margin + imageSize.height() + 1, shadow, shadow);
382 QRadialGradient g(rect.topRight(), shadow - 1);
383 g.setColorAt(0, dark);
384 g.setColorAt(1, light);
385 p.fillRect(rect, g);
386 }
387
388 p.end();
389
390 return QPixmap::fromImage(dest);
391 }
392
loadFrom(const QString & path,bool resourceFile,const QString & uiExtension,const QString & selectedItem,QTreeWidgetItem * & selectedItemFound)393 void NewFormWidget::loadFrom(const QString &path, bool resourceFile, const QString &uiExtension,
394 const QString &selectedItem, QTreeWidgetItem *&selectedItemFound)
395 {
396 const QDir dir(path);
397
398 if (!dir.exists())
399 return;
400
401 // Iterate through the directory and add the templates
402 const QFileInfoList list = dir.entryInfoList(QStringList(QStringLiteral("*.") + uiExtension),
403 QDir::Files);
404
405 if (list.isEmpty())
406 return;
407
408 const QChar separator = resourceFile ? QChar(QLatin1Char('/'))
409 : QDir::separator();
410 QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget);
411 root->setFlags(root->flags() & ~Qt::ItemIsSelectable);
412 // Try to get something that is easy to read.
413 QString visiblePath = path;
414 int index = visiblePath.lastIndexOf(separator);
415 if (index != -1) {
416 // try to find a second slash, just to be a bit better.
417 const int index2 = visiblePath.lastIndexOf(separator, index - 1);
418 if (index2 != -1)
419 index = index2;
420 visiblePath = visiblePath.mid(index + 1);
421 visiblePath = QDir::toNativeSeparators(visiblePath);
422 }
423
424 const QChar underscore = QLatin1Char('_');
425 const QChar blank = QLatin1Char(' ');
426 root->setText(0, visiblePath.replace(underscore, blank));
427 root->setToolTip(0, path);
428
429 const QFileInfoList::const_iterator lcend = list.constEnd();
430 for (QFileInfoList::const_iterator it = list.constBegin(); it != lcend; ++it) {
431 if (!it->isFile())
432 continue;
433
434 QTreeWidgetItem *item = new QTreeWidgetItem(root);
435 const QString text = it->baseName().replace(underscore, blank);
436 if (selectedItemFound == nullptr && text == selectedItem)
437 selectedItemFound = item;
438 item->setText(0, text);
439 item->setData(0, TemplateNameRole, it->absoluteFilePath());
440 }
441 }
442
loadFrom(const QString & title,const QStringList & nameList,const QString & selectedItem,QTreeWidgetItem * & selectedItemFound)443 void NewFormWidget::loadFrom(const QString &title, const QStringList &nameList,
444 const QString &selectedItem, QTreeWidgetItem *&selectedItemFound)
445 {
446 if (nameList.isEmpty())
447 return;
448 QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget);
449 root->setFlags(root->flags() & ~Qt::ItemIsSelectable);
450 root->setText(0, title);
451 const QStringList::const_iterator cend = nameList.constEnd();
452 for (QStringList::const_iterator it = nameList.constBegin(); it != cend; ++it) {
453 const QString text = *it;
454 QTreeWidgetItem *item = new QTreeWidgetItem(root);
455 item->setText(0, text);
456 if (selectedItemFound == nullptr && text == selectedItem)
457 selectedItemFound = item;
458 item->setData(0, ClassNameRole, *it);
459 }
460 }
461
on_treeWidget_itemPressed(QTreeWidgetItem * item)462 void NewFormWidget::on_treeWidget_itemPressed(QTreeWidgetItem *item)
463 {
464 if (item && !item->parent())
465 item->setExpanded(!item->isExpanded());
466 }
467
templateSize() const468 QSize NewFormWidget::templateSize() const
469 {
470 return m_ui->sizeComboBox->itemData(m_ui->sizeComboBox->currentIndex()).toSize();
471 }
472
setTemplateSize(const QSize & s)473 void NewFormWidget::setTemplateSize(const QSize &s)
474 {
475 const int index = s.isNull() ? 0 : m_ui->sizeComboBox->findData(s);
476 if (index != -1)
477 m_ui->sizeComboBox->setCurrentIndex(index);
478 }
479
readAll(const QString & fileName,QString * errorMessage)480 static QString readAll(const QString &fileName, QString *errorMessage)
481 {
482 QFile file(fileName);
483 if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
484 *errorMessage = NewFormWidget::tr("Unable to open the form template file '%1': %2").arg(fileName, file.errorString());
485 return QString();
486 }
487 return QString::fromUtf8(file.readAll());
488 }
489
itemToTemplate(const QTreeWidgetItem * item,QString * errorMessage) const490 QString NewFormWidget::itemToTemplate(const QTreeWidgetItem *item, QString *errorMessage) const
491 {
492 const QSize size = templateSize();
493 // file name or string contents?
494 const QVariant templateFileName = item->data(0, TemplateNameRole);
495 if (templateFileName.type() == QVariant::String) {
496 const QString fileName = templateFileName.toString();
497 // No fixed size: just open.
498 if (size.isNull())
499 return readAll(fileName, errorMessage);
500 // try to find a file matching the size, like "../640x480/xx.ui"
501 const QFileInfo fiBase(fileName);
502 QString sizeFileName;
503 QTextStream(&sizeFileName) << fiBase.path() << QDir::separator()
504 << size.width() << QLatin1Char('x') << size.height() << QDir::separator()
505 << fiBase.fileName();
506 if (QFileInfo(sizeFileName).isFile())
507 return readAll(sizeFileName, errorMessage);
508 // Nothing found, scale via DOM/temporary file
509 QString contents = readAll(fileName, errorMessage);
510 if (!contents.isEmpty())
511 contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false);
512 return contents;
513 }
514 // Content.
515 const QString className = item->data(0, ClassNameRole).toString();
516 QString contents = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className));
517 if (!size.isNull())
518 contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false);
519 return contents;
520 }
521
slotDeviceProfileIndexChanged(int idx)522 void NewFormWidget::slotDeviceProfileIndexChanged(int idx)
523 {
524 // Store index for form windows to take effect and refresh pixmap
525 QDesignerSharedSettings settings(m_core);
526 settings.setCurrentDeviceProfileIndex(idx - profileComboIndexOffset);
527 showCurrentItemPixmap();
528 }
529
profileComboIndex() const530 int NewFormWidget::profileComboIndex() const
531 {
532 return m_ui->profileComboBox->currentIndex();
533 }
534
currentDeviceProfile() const535 qdesigner_internal::DeviceProfile NewFormWidget::currentDeviceProfile() const
536 {
537 const int ci = profileComboIndex();
538 if (ci > 0)
539 return m_deviceProfiles.at(ci - profileComboIndexOffset);
540 return qdesigner_internal::DeviceProfile();
541 }
542
hasCurrentTemplate() const543 bool NewFormWidget::hasCurrentTemplate() const
544 {
545 return m_currentItem != nullptr;
546 }
547
currentTemplateI(QString * ptrToErrorMessage)548 QString NewFormWidget::currentTemplateI(QString *ptrToErrorMessage)
549 {
550 if (m_currentItem == nullptr) {
551 *ptrToErrorMessage = tr("Internal error: No template selected.");
552 return QString();
553 }
554 const QString contents = itemToTemplate(m_currentItem, ptrToErrorMessage);
555 if (contents.isEmpty())
556 return contents;
557
558 m_acceptedItem = m_currentItem;
559 return contents;
560 }
561
currentTemplate(QString * ptrToErrorMessage)562 QString NewFormWidget::currentTemplate(QString *ptrToErrorMessage)
563 {
564 if (ptrToErrorMessage)
565 return currentTemplateI(ptrToErrorMessage);
566 // Do not loose the error
567 QString errorMessage;
568 const QString contents = currentTemplateI(&errorMessage);
569 if (!errorMessage.isEmpty())
570 qWarning("%s", errorMessage.toUtf8().constData());
571 return contents;
572 }
573
574 }
575
576 QT_END_NAMESPACE
577