diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tools/designer/src/lib/shared/newformwidget.cpp | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'tools/designer/src/lib/shared/newformwidget.cpp')
-rw-r--r-- | tools/designer/src/lib/shared/newformwidget.cpp | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/tools/designer/src/lib/shared/newformwidget.cpp b/tools/designer/src/lib/shared/newformwidget.cpp new file mode 100644 index 0000000..d79d77a --- /dev/null +++ b/tools/designer/src/lib/shared/newformwidget.cpp @@ -0,0 +1,587 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "newformwidget_p.h" +#include "ui_newformwidget.h" +#include "qdesigner_formbuilder_p.h" +#include "sheet_delegate_p.h" +#include "widgetdatabase_p.h" +#include "shared_settings_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> +#include <QtCore/QByteArray> +#include <QtCore/QBuffer> +#include <QtCore/QDir> +#include <QtCore/QTextStream> + +#include <QtGui/QHeaderView> +#include <QtGui/QTreeWidgetItem> +#include <QtGui/QPainter> +#include <QtGui/QPushButton> + +QT_BEGIN_NAMESPACE + +enum { profileComboIndexOffset = 1 }; +enum { debugNewFormWidget = 0 }; + +enum NewForm_CustomRole { + // File name (templates from resources, paths) + TemplateNameRole = Qt::UserRole + 100, + // Class name (widgets from Widget data base) + ClassNameRole = Qt::UserRole + 101 +}; + +static const char *newFormObjectNameC = "Form"; + +// Create a form name for an arbitrary class. If it is Qt, qtify it, +// else return "Form". +static QString formName(const QString &className) +{ + if (!className.startsWith(QLatin1Char('Q'))) + return QLatin1String(newFormObjectNameC); + QString rc = className; + rc.remove(0, 1); + return rc; +} + +namespace qdesigner_internal { + +struct TemplateSize { + const char *name; + int width; + int height; +}; + +static const struct TemplateSize templateSizes[] = +{ + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "Default size"), 0, 0 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA portrait (240x320)"), 240, 320 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA landscape (320x240)"), 320, 240 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA portrait (480x640)"), 480, 640 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA landscape (640x480)"), 640, 480 } +}; + +/* -------------- NewForm dialog. + * Designer takes new form templates from: + * 1) Files located in directories specified in resources + * 2) Files located in directories specified as user templates + * 3) XML from container widgets deemed usable for form templates by the widget + * database + * 4) XML from custom container widgets deemed usable for form templates by the + * widget database + * + * The widget database provides helper functions to obtain lists of names + * and xml for 3,4. + * + * Fixed-size forms for embedded platforms are obtained as follows: + * 1) If the origin is a file: + * - Check if the file exists in the subdirectory "/<width>x<height>/" of + * the path (currently the case for the dialog box because the button box + * needs to be positioned) + * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine. + * 2) If the origin is XML: + * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine. + * + * The tree widget item roles indicate which type of entry it is + * (TemplateNameRole = file name 1,2, ClassNameRole = class name 3,4) + */ + +NewFormWidget::NewFormWidget(QDesignerFormEditorInterface *core, QWidget *parentWidget) : + QDesignerNewFormWidgetInterface(parentWidget), + m_core(core), + m_ui(new Ui::NewFormWidget), + m_currentItem(0), + m_acceptedItem(0) +{ + typedef QList<qdesigner_internal::DeviceProfile> DeviceProfileList; + + m_ui->setupUi(this); + m_ui->treeWidget->setItemDelegate(new qdesigner_internal::SheetDelegate(m_ui->treeWidget, this)); + m_ui->treeWidget->header()->hide(); + m_ui->treeWidget->header()->setStretchLastSection(true); + m_ui->lblPreview->setBackgroundRole(QPalette::Base); + QDesignerSharedSettings settings(m_core); + + QString uiExtension = QLatin1String("ui"); + QString templatePath = QLatin1String(":/trolltech/designer/templates/forms"); + + QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core); + if (lang) { + templatePath = QLatin1String(":/templates/forms"); + uiExtension = lang->uiExtension(); + } + + // Resource templates + const QString formTemplate = settings.formTemplate(); + QTreeWidgetItem *selectedItem = 0; + loadFrom(templatePath, true, uiExtension, formTemplate, selectedItem); + // Additional template paths + const QStringList formTemplatePaths = settings.formTemplatePaths(); + const QStringList::const_iterator ftcend = formTemplatePaths.constEnd(); + for (QStringList::const_iterator it = formTemplatePaths.constBegin(); it != ftcend; ++it) + loadFrom(*it, false, uiExtension, formTemplate, selectedItem); + + // Widgets/custom widgets + if (!lang) { + //: New Form Dialog Categories + loadFrom(tr("Widgets"), qdesigner_internal::WidgetDataBase::formWidgetClasses(core), formTemplate, selectedItem); + loadFrom(tr("Custom Widgets"), qdesigner_internal::WidgetDataBase::customFormWidgetClasses(core), formTemplate, selectedItem); + } + + // Still no selection - default to first item + if (selectedItem == 0 && m_ui->treeWidget->topLevelItemCount() != 0) { + QTreeWidgetItem *firstTopLevel = m_ui->treeWidget->topLevelItem(0); + if (firstTopLevel->childCount() > 0) + selectedItem = firstTopLevel->child(0); + } + + // Open parent, select and make visible + if (selectedItem) { + m_ui->treeWidget->setCurrentItem(selectedItem); + m_ui->treeWidget->setItemSelected(selectedItem, true); + m_ui->treeWidget->scrollToItem(selectedItem->parent()); + } + // Fill profile combo + m_deviceProfiles = settings.deviceProfiles(); + m_ui->profileComboBox->addItem(tr("None")); + connect(m_ui->profileComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotDeviceProfileIndexChanged(int))); + if (m_deviceProfiles.empty()) { + m_ui->profileComboBox->setEnabled(false); + } else { + const DeviceProfileList::const_iterator dcend = m_deviceProfiles.constEnd(); + for (DeviceProfileList::const_iterator it = m_deviceProfiles.constBegin(); it != dcend; ++it) + m_ui->profileComboBox->addItem(it->name()); + const int ci = settings.currentDeviceProfileIndex(); + if (ci >= 0) + m_ui->profileComboBox->setCurrentIndex(ci + profileComboIndexOffset); + } + // Fill size combo + const int sizeCount = sizeof(templateSizes)/ sizeof(TemplateSize); + for (int i = 0; i < sizeCount; i++) { + const QSize size = QSize(templateSizes[i].width, templateSizes[i].height); + m_ui->sizeComboBox->addItem(tr(templateSizes[i].name), size); + } + + setTemplateSize(settings.newFormSize()); + + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << "Leaving"; +} + +NewFormWidget::~NewFormWidget() +{ + QDesignerSharedSettings settings (m_core); + settings.setNewFormSize(templateSize()); + // Do not change previously stored item if dialog was rejected + if (m_acceptedItem) + settings.setFormTemplate(m_acceptedItem->text(0)); + delete m_ui; +} + +void NewFormWidget::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *) +{ + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << current; + if (!current) + return; + + if (!current->parent()) { // Top level item: Ensure expanded when browsing down + return; + } + + m_currentItem = current; + + emit currentTemplateChanged(showCurrentItemPixmap()); +} + +bool NewFormWidget::showCurrentItemPixmap() +{ + bool rc = false; + if (m_currentItem) { + const QPixmap pixmap = formPreviewPixmap(m_currentItem); + if (pixmap.isNull()) { + m_ui->lblPreview->setText(tr("Error loading form")); + } else { + m_ui->lblPreview->setPixmap(pixmap); + rc = true; + } + } + return rc; +} + +void NewFormWidget::on_treeWidget_itemActivated(QTreeWidgetItem *item) +{ + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << item; + + if (item->data(0, TemplateNameRole).isValid() || item->data(0, ClassNameRole).isValid()) + emit templateActivated(); +} + +QPixmap NewFormWidget::formPreviewPixmap(const QTreeWidgetItem *item) +{ + // Cache pixmaps per item/device profile + const ItemPixmapCacheKey cacheKey(item, profileComboIndex()); + ItemPixmapCache::iterator it = m_itemPixmapCache.find(cacheKey); + if (it == m_itemPixmapCache.end()) { + // file or string? + const QVariant fileName = item->data(0, TemplateNameRole); + QPixmap rc; + if (fileName.type() == QVariant::String) { + rc = formPreviewPixmap(fileName.toString()); + } else { + const QVariant classNameV = item->data(0, ClassNameRole); + Q_ASSERT(classNameV.type() == QVariant::String); + const QString className = classNameV.toString(); + QByteArray data = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className)).toUtf8(); + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly); + rc = formPreviewPixmap(buffer); + } + if (rc.isNull()) // Retry invalid ones + return rc; + it = m_itemPixmapCache.insert(cacheKey, rc); + } + return it.value(); +} + +QPixmap NewFormWidget::formPreviewPixmap(const QString &fileName) const +{ + QFile f(fileName); + if (f.open(QFile::ReadOnly)) { + QFileInfo fi(fileName); + const QPixmap rc = formPreviewPixmap(f, fi.absolutePath()); + f.close(); + return rc; + } + qWarning() << "The file " << fileName << " could not be opened: " << f.errorString(); + return QPixmap(); +} + +QImage NewFormWidget::grabForm(QDesignerFormEditorInterface *core, + QIODevice &file, + const QString &workingDir, + const qdesigner_internal::DeviceProfile &dp) +{ + qdesigner_internal::QDesignerFormBuilder formBuilder(core, + qdesigner_internal::QDesignerFormBuilder::DisableScripts, + dp); + if (!workingDir.isEmpty()) + formBuilder.setWorkingDirectory(workingDir); + + QWidget *widget = formBuilder.load(&file, 0); + if (!widget) + return QImage(); + + const QPixmap pixmap = QPixmap::grabWidget(widget); + widget->deleteLater(); + return pixmap.toImage(); +} + +QPixmap NewFormWidget::formPreviewPixmap(QIODevice &file, const QString &workingDir) const +{ + const int margin = 7; + const int shadow = 7; + const int previewSize = 256; + + const QImage wimage = grabForm(m_core, file, workingDir, currentDeviceProfile()); + if (wimage.isNull()) + return QPixmap(); + const QImage image = wimage.scaled(previewSize - margin * 2, previewSize - margin * 2, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + + QImage dest(previewSize, previewSize, QImage::Format_ARGB32_Premultiplied); + dest.fill(0); + + QPainter p(&dest); + p.drawImage(margin, margin, image); + + p.setPen(QPen(palette().brush(QPalette::WindowText), 0)); + + p.drawRect(margin-1, margin-1, image.width() + 1, image.height() + 1); + + const QColor dark(Qt::darkGray); + const QColor light(Qt::transparent); + + // right shadow + { + const QRect rect(margin + image.width() + 1, margin + shadow, shadow, image.height() - shadow + 1); + QLinearGradient lg(rect.topLeft(), rect.topRight()); + lg.setColorAt(0, dark); + lg.setColorAt(1, light); + p.fillRect(rect, lg); + } + + // bottom shadow + { + const QRect rect(margin + shadow, margin + image.height() + 1, image.width() - shadow + 1, shadow); + QLinearGradient lg(rect.topLeft(), rect.bottomLeft()); + lg.setColorAt(0, dark); + lg.setColorAt(1, light); + p.fillRect(rect, lg); + } + + // bottom/right corner shadow + { + const QRect rect(margin + image.width() + 1, margin + image.height() + 1, shadow, shadow); + QRadialGradient g(rect.topLeft(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + // top/right corner + { + const QRect rect(margin + image.width() + 1, margin, shadow, shadow); + QRadialGradient g(rect.bottomLeft(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + // bottom/left corner + { + const QRect rect(margin, margin + image.height() + 1, shadow, shadow); + QRadialGradient g(rect.topRight(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + p.end(); + + return QPixmap::fromImage(dest); +} + +void NewFormWidget::loadFrom(const QString &path, bool resourceFile, const QString &uiExtension, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound) +{ + const QDir dir(path); + + if (!dir.exists()) + return; + + // Iterate through the directory and add the templates + const QFileInfoList list = dir.entryInfoList(QStringList(QLatin1String("*.") + uiExtension), + QDir::Files); + + if (list.isEmpty()) + return; + + const QChar separator = resourceFile ? QChar(QLatin1Char('/')) + : QDir::separator(); + QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget); + root->setFlags(root->flags() & ~Qt::ItemIsSelectable); + // Try to get something that is easy to read. + QString visiblePath = path; + int index = visiblePath.lastIndexOf(separator); + if (index != -1) { + // try to find a second slash, just to be a bit better. + const int index2 = visiblePath.lastIndexOf(separator, index - 1); + if (index2 != -1) + index = index2; + visiblePath = visiblePath.mid(index + 1); + visiblePath = QDir::toNativeSeparators(visiblePath); + } + + const QChar underscore = QLatin1Char('_'); + const QChar blank = QLatin1Char(' '); + root->setText(0, visiblePath.replace(underscore, blank)); + root->setToolTip(0, path); + + const QFileInfoList::const_iterator lcend = list.constEnd(); + for (QFileInfoList::const_iterator it = list.constBegin(); it != lcend; ++it) { + if (!it->isFile()) + continue; + + QTreeWidgetItem *item = new QTreeWidgetItem(root); + const QString text = it->baseName().replace(underscore, blank); + if (selectedItemFound == 0 && text == selectedItem) + selectedItemFound = item; + item->setText(0, text); + item->setData(0, TemplateNameRole, it->absoluteFilePath()); + } +} + +void NewFormWidget::loadFrom(const QString &title, const QStringList &nameList, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound) +{ + if (nameList.empty()) + return; + QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget); + root->setFlags(root->flags() & ~Qt::ItemIsSelectable); + root->setText(0, title); + const QStringList::const_iterator cend = nameList.constEnd(); + for (QStringList::const_iterator it = nameList.constBegin(); it != cend; ++it) { + const QString text = *it; + QTreeWidgetItem *item = new QTreeWidgetItem(root); + item->setText(0, text); + if (selectedItemFound == 0 && text == selectedItem) + selectedItemFound = item; + item->setData(0, ClassNameRole, *it); + } +} + +void NewFormWidget::on_treeWidget_itemPressed(QTreeWidgetItem *item) +{ + if (item && !item->parent()) + m_ui->treeWidget->setItemExpanded(item, !m_ui->treeWidget->isItemExpanded(item)); +} + +QSize NewFormWidget::templateSize() const +{ + return m_ui->sizeComboBox->itemData(m_ui->sizeComboBox->currentIndex()).toSize(); +} + +void NewFormWidget::setTemplateSize(const QSize &s) +{ + const int index = s.isNull() ? 0 : m_ui->sizeComboBox->findData(s); + if (index != -1) + m_ui->sizeComboBox->setCurrentIndex(index); +} + +static QString readAll(const QString &fileName, QString *errorMessage) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = NewFormWidget::tr("Unable to open the form template file '%1': %2").arg(fileName, file.errorString()); + return QString(); + } + return QString::fromUtf8(file.readAll()); +} + +QString NewFormWidget::itemToTemplate(const QTreeWidgetItem *item, QString *errorMessage) const +{ + const QSize size = templateSize(); + // file name or string contents? + const QVariant templateFileName = item->data(0, TemplateNameRole); + if (templateFileName.type() == QVariant::String) { + const QString fileName = templateFileName.toString(); + // No fixed size: just open. + if (size.isNull()) + return readAll(fileName, errorMessage); + // try to find a file matching the size, like "../640x480/xx.ui" + const QFileInfo fiBase(fileName); + QString sizeFileName; + QTextStream(&sizeFileName) << fiBase.path() << QDir::separator() + << size.width() << QLatin1Char('x') << size.height() << QDir::separator() + << fiBase.fileName(); + if (QFileInfo(sizeFileName).isFile()) + return readAll(sizeFileName, errorMessage); + // Nothing found, scale via DOM/temporary file + QString contents = readAll(fileName, errorMessage); + if (!contents.isEmpty()) + contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false); + return contents; + } + // Content. + const QString className = item->data(0, ClassNameRole).toString(); + QString contents = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className)); + if (!size.isNull()) + contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false); + return contents; +} + +void NewFormWidget::slotDeviceProfileIndexChanged(int idx) +{ + // Store index for form windows to take effect and refresh pixmap + QDesignerSharedSettings settings(m_core); + settings.setCurrentDeviceProfileIndex(idx - profileComboIndexOffset); + showCurrentItemPixmap(); +} + +int NewFormWidget::profileComboIndex() const +{ + return m_ui->profileComboBox->currentIndex(); +} + +qdesigner_internal::DeviceProfile NewFormWidget::currentDeviceProfile() const +{ + const int ci = profileComboIndex(); + if (ci > 0) + return m_deviceProfiles.at(ci - profileComboIndexOffset); + return qdesigner_internal::DeviceProfile(); +} + +bool NewFormWidget::hasCurrentTemplate() const +{ + return m_currentItem != 0; +} + +QString NewFormWidget::currentTemplateI(QString *ptrToErrorMessage) +{ + if (m_currentItem == 0) { + *ptrToErrorMessage = tr("Internal error: No template selected."); + return QString(); + } + const QString contents = itemToTemplate(m_currentItem, ptrToErrorMessage); + if (contents.isEmpty()) + return contents; + + m_acceptedItem = m_currentItem; + return contents; +} + +QString NewFormWidget::currentTemplate(QString *ptrToErrorMessage) +{ + if (ptrToErrorMessage) + return currentTemplateI(ptrToErrorMessage); + // Do not loose the error + QString errorMessage; + const QString contents = currentTemplateI(&errorMessage); + if (!errorMessage.isEmpty()) + qWarning("%s", errorMessage.toUtf8().constData()); + return contents; +} + +} + +QT_END_NAMESPACE |