tools/designer/src/lib/shared/qdesigner_promotion.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Designer of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    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 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qdesigner_promotion_p.h"
       
    43 #include "widgetdatabase_p.h"
       
    44 #include "metadatabase_p.h"
       
    45 #include "widgetdatabase_p.h"
       
    46 
       
    47 #include <QtDesigner/QDesignerFormEditorInterface>
       
    48 #include <QtDesigner/QDesignerFormWindowInterface>
       
    49 #include <QtDesigner/QDesignerFormWindowManagerInterface>
       
    50 #include <QtDesigner/QDesignerObjectInspectorInterface>
       
    51 #include <QtDesigner/QDesignerWidgetBoxInterface>
       
    52 #include <QtDesigner/QDesignerWidgetDataBaseInterface>
       
    53 
       
    54 #include <QtCore/QMap>
       
    55 #include <QtCore/QCoreApplication>
       
    56 #include <qdebug.h>
       
    57 
       
    58 QT_BEGIN_NAMESPACE
       
    59 
       
    60 namespace {
       
    61     // Return a set of on-promotable classes
       
    62     const QSet<QString> &nonPromotableClasses() {
       
    63         static QSet<QString> rc;
       
    64         if (rc.empty()) {
       
    65             rc.insert(QLatin1String("Line"));
       
    66             rc.insert(QLatin1String("QAction"));
       
    67             rc.insert(QLatin1String("Spacer"));
       
    68             rc.insert(QLatin1String("QMainWindow"));
       
    69             rc.insert(QLatin1String("QDialog"));
       
    70             rc.insert(QLatin1String("QWorkspace"));
       
    71             rc.insert(QLatin1String("QMdiArea"));
       
    72             rc.insert(QLatin1String("QMdiSubWindow"));
       
    73         }
       
    74         return rc;
       
    75     }
       
    76 
       
    77     // Return widget database index of a promoted class or -1 with error message
       
    78     int promotedWidgetDataBaseIndex(const QDesignerWidgetDataBaseInterface *widgetDataBase,
       
    79                                                                  const QString &className,
       
    80                                                                  QString *errorMessage) {
       
    81         const int index = widgetDataBase->indexOfClassName(className);
       
    82         if (index == -1 || !widgetDataBase->item(index)->isPromoted()) {
       
    83             *errorMessage = QCoreApplication::tr("%1 is not a promoted class.").arg(className);
       
    84             return -1;
       
    85         }
       
    86         return index;
       
    87     }
       
    88 
       
    89     // Return widget database item of a promoted class or 0 with error message
       
    90     QDesignerWidgetDataBaseItemInterface *promotedWidgetDataBaseItem(const QDesignerWidgetDataBaseInterface *widgetDataBase,
       
    91                                                                      const QString &className,
       
    92                                                                      QString *errorMessage) {
       
    93 
       
    94         const int index =  promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage);
       
    95         if (index == -1)
       
    96             return 0;
       
    97         return widgetDataBase->item(index);
       
    98     }
       
    99 
       
   100     // extract class name from xml  "<widget class="QWidget" ...>". Quite a hack.
       
   101     QString classNameFromXml(QString xml) {
       
   102         static const QString tag = QLatin1String("class=\"");
       
   103         const int pos = xml.indexOf(tag);
       
   104         if (pos == -1)
       
   105             return QString();
       
   106         xml.remove(0, pos + tag.size());
       
   107         const int closingPos = xml.indexOf(QLatin1Char('"'));
       
   108         if (closingPos == -1)
       
   109             return QString();
       
   110         xml.remove(closingPos, xml.size() - closingPos);
       
   111         return xml;
       
   112     }
       
   113 
       
   114     // return a list of class names in the scratch pad
       
   115     QStringList getScratchPadClasses(const QDesignerWidgetBoxInterface *wb) {
       
   116         QStringList rc;
       
   117         const int catCount =  wb->categoryCount();
       
   118         for (int c = 0; c <  catCount; c++) {
       
   119             const QDesignerWidgetBoxInterface::Category category = wb->category(c);
       
   120             if (category.type() == QDesignerWidgetBoxInterface::Category::Scratchpad) {
       
   121                 const int widgetCount = category.widgetCount();
       
   122                 for (int w = 0; w < widgetCount; w++) {
       
   123                     const QString className = classNameFromXml( category.widget(w).domXml());
       
   124                     if (!className.isEmpty())
       
   125                         rc += className;
       
   126                 }
       
   127             }
       
   128         }
       
   129         return rc;
       
   130     }
       
   131 }
       
   132 
       
   133 namespace qdesigner_internal {
       
   134 
       
   135     QDesignerPromotion::QDesignerPromotion(QDesignerFormEditorInterface *core) :
       
   136         m_core(core)  {
       
   137     }
       
   138 
       
   139     bool  QDesignerPromotion::addPromotedClass(const QString &baseClass,
       
   140                                                const QString &className,
       
   141                                                const QString &includeFile,
       
   142                                                QString *errorMessage)
       
   143     {
       
   144         QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase();
       
   145         const int baseClassIndex = widgetDataBase->indexOfClassName(baseClass);
       
   146 
       
   147         if (baseClassIndex == -1) {
       
   148             *errorMessage = QCoreApplication::tr("The base class %1 is invalid.").arg(baseClass);
       
   149             return false;
       
   150         }
       
   151 
       
   152         const int existingClassIndex = widgetDataBase->indexOfClassName(className);
       
   153 
       
   154         if (existingClassIndex != -1) {
       
   155             *errorMessage = QCoreApplication::tr("The class %1 already exists.").arg(className);
       
   156             return false;
       
   157         }
       
   158         // Clone derived item.
       
   159         QDesignerWidgetDataBaseItemInterface *promotedItem = WidgetDataBaseItem::clone(widgetDataBase->item(baseClassIndex));
       
   160         // Also inherit the container flag in case of QWidget-derived classes
       
   161         // as it is most likely intended for stacked pages.
       
   162         // set new props
       
   163         promotedItem->setName(className);
       
   164         promotedItem->setGroup(QCoreApplication::tr("Promoted Widgets"));
       
   165         promotedItem->setCustom(true);
       
   166         promotedItem->setPromoted(true);
       
   167         promotedItem->setExtends(baseClass);
       
   168         promotedItem->setIncludeFile(includeFile);
       
   169         widgetDataBase->append(promotedItem);
       
   170         return true;
       
   171     }
       
   172 
       
   173     QList<QDesignerWidgetDataBaseItemInterface *> QDesignerPromotion::promotionBaseClasses() const
       
   174     {
       
   175         typedef QMap<QString, QDesignerWidgetDataBaseItemInterface *> SortedDatabaseItemMap;
       
   176         SortedDatabaseItemMap sortedDatabaseItemMap;
       
   177 
       
   178         QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase();
       
   179 
       
   180         const int cnt = widgetDataBase->count();
       
   181         for (int i = 0; i <  cnt; i++) {
       
   182             QDesignerWidgetDataBaseItemInterface *dbItem = widgetDataBase->item(i);
       
   183             if (canBePromoted(dbItem)) {
       
   184                 sortedDatabaseItemMap.insert(dbItem->name(), dbItem);
       
   185             }
       
   186         }
       
   187 
       
   188         return sortedDatabaseItemMap.values();
       
   189     }
       
   190 
       
   191 
       
   192     bool QDesignerPromotion::canBePromoted(const QDesignerWidgetDataBaseItemInterface *dbItem) const
       
   193     {
       
   194         if (dbItem->isPromoted() ||  !dbItem->extends().isEmpty())
       
   195             return false;
       
   196 
       
   197         const QString name = dbItem->name();
       
   198 
       
   199         if (nonPromotableClasses().contains(name))
       
   200             return false;
       
   201 
       
   202         if (name.startsWith(QLatin1String("QDesigner")) ||
       
   203             name.startsWith(QLatin1String("QLayout")))
       
   204             return false;
       
   205 
       
   206         return true;
       
   207     }
       
   208 
       
   209     QDesignerPromotion::PromotedClasses QDesignerPromotion::promotedClasses()  const
       
   210     {
       
   211         typedef QMap<QString, QDesignerWidgetDataBaseItemInterface *> ClassNameItemMap;
       
   212         // A map containing base classes and their promoted classes.
       
   213         typedef QMap<QString, ClassNameItemMap> BaseClassPromotedMap;
       
   214 
       
   215         BaseClassPromotedMap baseClassPromotedMap;
       
   216 
       
   217         QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase();
       
   218         // Look for promoted classes and insert into map according to base class.
       
   219         const  int cnt = widgetDataBase->count();
       
   220         for (int i = 0; i < cnt; i++) {
       
   221             QDesignerWidgetDataBaseItemInterface *dbItem = widgetDataBase->item(i);
       
   222             if (dbItem->isPromoted()) {
       
   223                 const QString baseClassName = dbItem->extends();
       
   224                 BaseClassPromotedMap::iterator it = baseClassPromotedMap.find(baseClassName);
       
   225                 if (it == baseClassPromotedMap.end()) {
       
   226                     it = baseClassPromotedMap.insert(baseClassName, ClassNameItemMap());
       
   227                 }
       
   228                 it.value().insert(dbItem->name(), dbItem);
       
   229             }
       
   230         }
       
   231         // convert map into list.
       
   232         PromotedClasses rc;
       
   233 
       
   234         if (baseClassPromotedMap.empty())
       
   235             return rc;
       
   236 
       
   237         const BaseClassPromotedMap::const_iterator bcend = baseClassPromotedMap.constEnd();
       
   238         for (BaseClassPromotedMap::const_iterator bit = baseClassPromotedMap.constBegin(); bit !=  bcend; ++bit) {
       
   239             const int baseIndex = widgetDataBase->indexOfClassName(bit.key());
       
   240             Q_ASSERT(baseIndex >= 0);
       
   241             QDesignerWidgetDataBaseItemInterface *baseItem = widgetDataBase->item(baseIndex);
       
   242             // promoted
       
   243             const ClassNameItemMap::const_iterator pcend = bit.value().constEnd();
       
   244             for (ClassNameItemMap::const_iterator pit = bit.value().constBegin(); pit != pcend; ++pit) {
       
   245                 PromotedClass item;
       
   246                 item.baseItem = baseItem;
       
   247                 item.promotedItem = pit.value();
       
   248                 rc.push_back(item);
       
   249             }
       
   250         }
       
   251 
       
   252         return rc;
       
   253     }
       
   254 
       
   255     QSet<QString> QDesignerPromotion::referencedPromotedClassNames()  const {
       
   256         QSet<QString> rc;
       
   257         const MetaDataBase *metaDataBase = qobject_cast<const MetaDataBase*>(m_core->metaDataBase());
       
   258         if (!metaDataBase)
       
   259             return rc;
       
   260 
       
   261         const QList<QObject*> objs = metaDataBase->objects();
       
   262         const QList<QObject*>::const_iterator cend = objs.constEnd();
       
   263         for ( QList<QObject*>::const_iterator it = objs.constBegin(); it != cend; ++it) {
       
   264             const QString customClass = metaDataBase->metaDataBaseItem(*it)->customClassName();
       
   265             if (!customClass.isEmpty())
       
   266                 rc.insert(customClass);
       
   267 
       
   268         }
       
   269         // check the scratchpad of the widget box
       
   270         if (QDesignerWidgetBoxInterface *widgetBox = m_core->widgetBox()) {
       
   271             const QStringList scratchPadClasses = getScratchPadClasses(widgetBox);
       
   272             if (!scratchPadClasses.empty()) {
       
   273                 // Check whether these are actually promoted
       
   274                 QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase();
       
   275                 QStringList::const_iterator cend = scratchPadClasses.constEnd();
       
   276                 for (QStringList::const_iterator it = scratchPadClasses.constBegin(); it != cend; ++it ) {
       
   277                     const int index = widgetDataBase->indexOfClassName(*it);
       
   278                     if (index != -1 && widgetDataBase->item(index)->isPromoted())
       
   279                         rc += *it;
       
   280                 }
       
   281             }
       
   282         }
       
   283         return rc;
       
   284     }
       
   285 
       
   286     bool QDesignerPromotion::removePromotedClass(const QString &className, QString *errorMessage) {
       
   287         // check if it exists and is promoted
       
   288         WidgetDataBase *widgetDataBase = qobject_cast<WidgetDataBase *>(m_core->widgetDataBase());
       
   289         if (!widgetDataBase) {
       
   290             *errorMessage = QCoreApplication::tr("The class %1 cannot be removed").arg(className);
       
   291             return false;
       
   292         }
       
   293 
       
   294         const int index = promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage);
       
   295         if (index == -1)
       
   296             return false;
       
   297 
       
   298         if (referencedPromotedClassNames().contains(className)) {
       
   299             *errorMessage = QCoreApplication::tr("The class %1 cannot be removed because it is still referenced.").arg(className);
       
   300             return false;
       
   301         }
       
   302         widgetDataBase->remove(index);
       
   303         return true;
       
   304     }
       
   305 
       
   306     bool QDesignerPromotion::changePromotedClassName(const QString &oldclassName, const QString &newClassName, QString *errorMessage) {
       
   307         const MetaDataBase *metaDataBase = qobject_cast<const MetaDataBase*>(m_core->metaDataBase());
       
   308         if (!metaDataBase) {
       
   309             *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed").arg(oldclassName);
       
   310             return false;
       
   311         }
       
   312         QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase();
       
   313 
       
   314         // check the new name
       
   315         if (newClassName.isEmpty()) {
       
   316             *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed to an empty name.").arg(oldclassName);
       
   317             return false;
       
   318         }
       
   319         const int existingIndex = widgetDataBase->indexOfClassName(newClassName);
       
   320         if (existingIndex != -1) {
       
   321             *errorMessage = QCoreApplication::tr("There is already a class named %1.").arg(newClassName);
       
   322             return false;
       
   323         }
       
   324         // Check old class
       
   325         QDesignerWidgetDataBaseItemInterface *dbItem = promotedWidgetDataBaseItem(widgetDataBase, oldclassName, errorMessage);
       
   326         if (!dbItem)
       
   327             return false;
       
   328 
       
   329         // Change the name in the data base and change all referencing objects in the meta database
       
   330         dbItem->setName(newClassName);
       
   331         bool foundReferences = false;
       
   332         foreach (QObject* object, metaDataBase->objects()) {
       
   333             MetaDataBaseItem *item =  metaDataBase->metaDataBaseItem(object);
       
   334             Q_ASSERT(item);
       
   335             if (item->customClassName() == oldclassName) {
       
   336                 item->setCustomClassName(newClassName);
       
   337                 foundReferences = true;
       
   338             }
       
   339         }
       
   340         // set state
       
   341         if (foundReferences)
       
   342             refreshObjectInspector();
       
   343 
       
   344         return true;
       
   345     }
       
   346 
       
   347     bool QDesignerPromotion::setPromotedClassIncludeFile(const QString &className, const QString &includeFile, QString *errorMessage) {
       
   348         // check file
       
   349         if (includeFile.isEmpty()) {
       
   350             *errorMessage = QCoreApplication::tr("Cannot set an empty include file.");
       
   351             return false;
       
   352         }
       
   353         // check item
       
   354         QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase();
       
   355         QDesignerWidgetDataBaseItemInterface *dbItem = promotedWidgetDataBaseItem(widgetDataBase, className, errorMessage);
       
   356         if (!dbItem)
       
   357             return false;
       
   358 
       
   359         dbItem->setIncludeFile(includeFile);
       
   360         return true;
       
   361     }
       
   362 
       
   363     void QDesignerPromotion::refreshObjectInspector() {
       
   364         if (QDesignerFormWindowManagerInterface *fwm = m_core->formWindowManager()) {
       
   365             if (QDesignerFormWindowInterface *fw = fwm->activeFormWindow())
       
   366                 if ( QDesignerObjectInspectorInterface *oi = m_core->objectInspector()) {
       
   367                     oi->setFormWindow(fw);
       
   368                 }
       
   369         }
       
   370     }
       
   371 }
       
   372 
       
   373 QT_END_NAMESPACE