File indexing completed on 2024-05-12 16:39:52

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
0003    Copyright (C) 2008-2010 Jarosław Staniek <staniek@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "WidgetTreeWidget.h"
0022 
0023 #include <QContextMenuEvent>
0024 #include <QDebug>
0025 
0026 #include <KIconEffect>
0027 #include <KLocalizedString>
0028 
0029 #include <KexiIcon.h>
0030 
0031 #include "objecttree.h"
0032 #include "form.h"
0033 #include "container.h"
0034 #include "widgetlibrary.h"
0035 #include "commands.h"
0036 
0037 using namespace KFormDesigner;
0038 
0039 class Q_DECL_HIDDEN WidgetTreeWidgetItem::Private
0040 {
0041 public:
0042     Private(ObjectTreeItem *data_,
0043             LoadTreeFlags loadTreeFlags_);
0044     ~Private();
0045 
0046     ObjectTreeItem *data;
0047     QString customSortingKey;
0048     LoadTreeFlags loadTreeFlags;
0049 };
0050 
0051 WidgetTreeWidgetItem::Private::Private(ObjectTreeItem *data_, LoadTreeFlags loadTreeFlags_)
0052     :data(data_), loadTreeFlags(loadTreeFlags_)
0053 {
0054 
0055 }
0056 
0057 WidgetTreeWidgetItem::Private::~Private()
0058 {
0059 
0060 }
0061 
0062 WidgetTreeWidgetItem::WidgetTreeWidgetItem(WidgetTreeWidgetItem *parent, ObjectTreeItem *data,
0063     LoadTreeFlags loadTreeFlags, int forcedTabPageIndex, const QString& forcedTabPageName)
0064         : QTreeWidgetItem(parent)
0065         ,d(new Private(data, loadTreeFlags))
0066 {
0067     init(forcedTabPageIndex, forcedTabPageName);
0068 }
0069 
0070 WidgetTreeWidgetItem::WidgetTreeWidgetItem(QTreeWidget *tree, ObjectTreeItem *data,
0071     LoadTreeFlags loadTreeFlags, int forcedTabPageIndex, const QString& forcedTabPageName)
0072         : QTreeWidgetItem(tree)
0073         , d(new Private(data, loadTreeFlags))
0074 {
0075     init(forcedTabPageIndex, forcedTabPageName);
0076 }
0077 
0078 WidgetTreeWidgetItem::~WidgetTreeWidgetItem()
0079 {
0080     delete d;
0081 }
0082 
0083 void WidgetTreeWidgetItem::init(int forcedTabPageIndex, const QString& forcedTabPageName)
0084 {
0085     if (d->data) {
0086         initTextAndIcon(forcedTabPageIndex, forcedTabPageName);
0087     }
0088 }
0089 
0090 void WidgetTreeWidgetItem::initTextAndIcon(int forcedTabPageIndex, const QString& forcedTabPageName)
0091 {
0092     QString itemName;
0093     QString itemClass;
0094     QString iconName;
0095     Qt::ItemFlags itemFlags = flags();
0096     WidgetTreeWidget *widgetTreeWidget = qobject_cast<WidgetTreeWidget*>(treeWidget());
0097     ObjectTreeItem* selectable = widgetTreeWidget ? widgetTreeWidget->selectableItem(d->data) : d->data;
0098     if (selectable != d->data) {
0099         //qDebug() << "****" << (d->loadTreeFlags & LoadTreeForAddedTabPage) << selectable->widget();
0100         if (qobject_cast<QTabWidget*>(selectable->widget())) {
0101             // tab widget's page
0102             const QTabWidget* tabWidget = qobject_cast<QTabWidget*>(selectable->widget());
0103             int tabIndex = tabWidget->indexOf(d->data->widget());
0104             if (tabIndex == -1 && (d->loadTreeFlags & LoadTreeForAddedTabPage)) { // tab appended
0105                 if (forcedTabPageIndex >= 0)
0106                     tabIndex = forcedTabPageIndex;
0107                 else
0108                     tabIndex = tabWidget->count();
0109             }
0110             //qDebug() << tabIndex;
0111             if (tabIndex >= 0) {
0112                 if (forcedTabPageName.isEmpty()) {
0113                     itemName = tabWidget->tabText(tabIndex);
0114                     if (itemName.isEmpty()) {
0115                         itemName = forcedTabPageName;
0116                         if (itemName.isEmpty())
0117                             itemName = xi18n("Page %1", tabIndex + 1);
0118                     }
0119                     else {
0120                         itemName.remove('&');
0121                     }
0122                 }
0123                 else
0124                     itemName = forcedTabPageName;
0125                 itemClass = xi18nc("Tab widget's page", "Tab Page");
0126                 d->customSortingKey = QString("tab%1").arg(tabIndex);
0127                 //qDebug() << "d->customSortingKey" << d->customSortingKey;
0128                 itemFlags |= Qt::ItemIsSelectable;
0129                 itemFlags ^= Qt::ItemIsSelectable;
0130                 iconName = KexiIconName("tabwidget-tab");
0131             }
0132         }
0133     }
0134     // defaults:
0135     if (itemName.isEmpty()) {
0136         itemName = d->data->name();
0137     }
0138     if (itemClass.isEmpty()) {
0139         itemClass = d->data->className();
0140     }
0141     if (iconName.isEmpty()) {
0142         if (widgetTreeWidget) {
0143             iconName = widgetTreeWidget->iconNameForClass(d->data->widget()->metaObject()->className());
0144         }
0145     }
0146     // set:
0147     if (itemFlags != flags()) {
0148         setFlags(itemFlags);
0149     }
0150     setText(0, itemName);
0151     setText(1, itemClass);
0152     if (!iconName.isEmpty()) {
0153         QPixmap icon(koSmallIconCStr(iconName));
0154         if (!(itemFlags & Qt::ItemIsSelectable)) {
0155             KIconEffect::semiTransparent(icon);
0156         }
0157         setIcon(0, icon);
0158     }
0159     if (!(itemFlags & Qt::ItemIsSelectable)) {
0160         setForeground(0, treeWidget()->palette().color(QPalette::Disabled, QPalette::Text));
0161         setForeground(1, treeWidget()->palette().color(QPalette::Disabled, QPalette::Text));
0162     }
0163 }
0164 
0165 QString WidgetTreeWidgetItem::name() const
0166 {
0167     if (d->data)
0168         return d->data->name();
0169     else
0170         return QString();
0171 }
0172 
0173 bool WidgetTreeWidgetItem::operator<( const QTreeWidgetItem & other ) const
0174 {
0175     const WidgetTreeWidgetItem *otherItem = dynamic_cast<const WidgetTreeWidgetItem*>(&other);
0176     if (!otherItem)
0177         return QTreeWidgetItem::operator<(other);
0178     return d->customSortingKey < otherItem->customSortingKey();
0179 }
0180 
0181 ObjectTreeItem* WidgetTreeWidgetItem::data() const
0182 {
0183     return d->data;
0184 }
0185 
0186 QString WidgetTreeWidgetItem::customSortingKey() const
0187 {
0188     return d->customSortingKey;
0189 }
0190 
0191 // WidgetTreeWidget itself -----------------------------------------------------------------------------------------------
0192 
0193 class Q_DECL_HIDDEN WidgetTreeWidget::Private
0194 {
0195 public:
0196     explicit Private(Options o);
0197     ~Private();
0198 
0199     Form *form;
0200     Options options;
0201 
0202     //! Used to temporarily disable slotSelectionChanged() when reloading contents in setForm().
0203     bool slotSelectionChanged_enabled;
0204     //! Used to temporarily disable selectWidget().
0205     bool selectWidget_enabled;
0206 };
0207 
0208 WidgetTreeWidget::Private::Private(Options o)
0209     :form(0), options(o), slotSelectionChanged_enabled(true), selectWidget_enabled(true)
0210 {
0211 
0212 }
0213 
0214 WidgetTreeWidget::Private::~Private()
0215 {
0216 
0217 }
0218 
0219 WidgetTreeWidget::WidgetTreeWidget(QWidget *parent, Options options)
0220     : QTreeWidget(parent), d(new Private(options))
0221 {
0222     setRootIsDecorated(false);
0223     setHeaderLabels(QStringList() << xi18n("Widget name") << xi18nc("Widget's type", "Type"));
0224     installEventFilter(this);
0225 
0226     if (!(d->options & DisableSelection)) {
0227         setSelectionMode(ExtendedSelection);
0228         connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()));
0229     }
0230 
0231     setAllColumnsShowFocus(true);
0232     setExpandsOnDoubleClick(false);
0233     setIndentation(indentation() / 2);
0234 }
0235 
0236 WidgetTreeWidget::~WidgetTreeWidget()
0237 {
0238     delete d;
0239 }
0240 
0241 WidgetTreeWidgetItem* WidgetTreeWidget::selectedItem() const
0242 {
0243     if (selectedItems().count() != 1)
0244         return 0;
0245     WidgetTreeWidgetItem *item = static_cast<WidgetTreeWidgetItem*>(selectedItems().first());
0246     return item;
0247 }
0248 
0249 QString WidgetTreeWidget::iconNameForClass(const QByteArray& classname) const
0250 {
0251     return d->form->library()->iconName(classname);
0252 }
0253 
0254 ObjectTreeItem* WidgetTreeWidget::selectableItem(ObjectTreeItem* item)
0255 {
0256     return d->form->library()->selectableItem(item);
0257 }
0258 
0259 void WidgetTreeWidget::handleContextMenuEvent(QContextMenuEvent* e)
0260 {
0261     if (!d->form)
0262         return;
0263     WidgetTreeWidgetItem *item = static_cast<WidgetTreeWidgetItem*>(itemAt(e->pos()));
0264     if (!item)
0265         return;
0266     WidgetTreeWidgetItem *newItem = static_cast<WidgetTreeWidgetItem*>(tryToAlterSelection(item));
0267     QWidget *w = newItem->data()->widget();
0268     if (!w)
0269         return;
0270     d->form->createContextMenu(w, d->form->activeContainer(), e->pos(), Form::WidgetTreeContextMenuTarget);
0271 }
0272 
0273 void WidgetTreeWidget::contextMenuEvent(QContextMenuEvent* e)
0274 {
0275     if (!(d->options & DisableContextMenu)) {
0276         handleContextMenuEvent(e);
0277     }
0278     QAbstractScrollArea::contextMenuEvent(e);
0279 }
0280 
0281 WidgetTreeWidgetItem* WidgetTreeWidget::findItem(const QString& name)
0282 {
0283     QTreeWidgetItemIterator it(this);
0284     while (*it) {
0285         WidgetTreeWidgetItem *item = static_cast<WidgetTreeWidgetItem*>(*it);
0286         if (item->name() == name) {
0287             return item;
0288         }
0289         ++it;
0290     }
0291     return 0;
0292 }
0293 
0294 WidgetTreeWidgetItem* WidgetTreeWidget::findItemByFirstColumn(const QString& text)
0295 {
0296     QTreeWidgetItemIterator it(this);
0297     while (*it) {
0298         WidgetTreeWidgetItem *item = static_cast<WidgetTreeWidgetItem*>(*it);
0299         if (item->text(0) == text) {
0300             return item;
0301         }
0302         ++it;
0303     }
0304     return 0;
0305 }
0306 
0307 void WidgetTreeWidget::selectWidget(QWidget *w, KFormDesigner::Form::WidgetSelectionFlags flags)
0308 {
0309     if (!d->selectWidget_enabled)
0310         return;
0311     blockSignals(true); // to avoid recursion
0312 
0313     if (!w) {
0314         clearSelection();
0315         blockSignals(false);
0316         return;
0317     }
0318 
0319     if (selectedItems().count() == 0) {
0320         flags |= Form::ReplacePreviousSelection;
0321     }
0322 
0323     if ((flags & Form::ReplacePreviousSelection))
0324         clearSelection();
0325 
0326     QTreeWidgetItem *item = findItem(w->objectName());
0327     if ((flags & Form::ReplacePreviousSelection)) {
0328         setCurrentItem(item);
0329         item->setSelected(true);
0330     } else {
0331         item->setSelected(true);
0332     }
0333 
0334     blockSignals(false);
0335 }
0336 
0337 void WidgetTreeWidget::selectWidgetForItem(QTreeWidgetItem *item)
0338 {
0339     WidgetTreeWidgetItem *it = dynamic_cast<WidgetTreeWidgetItem*>(item);
0340     if (!it)
0341         return;
0342     QWidget *w = it->data()->widget();
0343     if (w && !d->form->selectedWidgets()->contains(w)) {
0344         d->form->selectWidget(w, Form::AddToPreviousSelection | Form::DontRaise | Form::LastSelection);
0345     }
0346 }
0347 
0348 void WidgetTreeWidget::activateTabPageIfNeeded(QTreeWidgetItem* item)
0349 {
0350     WidgetTreeWidgetItem *childItem = dynamic_cast<WidgetTreeWidgetItem*>(item);
0351     if (!childItem)
0352         return;
0353     WidgetTreeWidgetItem *parentItem = dynamic_cast<WidgetTreeWidgetItem*>(item->parent());
0354     while (childItem && parentItem) {
0355         if (parentItem && qobject_cast<QTabWidget*>(parentItem->data()->widget())) {
0356             qobject_cast<QTabWidget*>(parentItem->data()->widget())->setCurrentWidget(
0357                 childItem->data()->widget());
0358         }
0359         childItem = parentItem;
0360         parentItem = dynamic_cast<WidgetTreeWidgetItem*>(parentItem->parent());
0361     }
0362 }
0363 
0364 QTreeWidgetItem* WidgetTreeWidget::tryToAlterSelection(QTreeWidgetItem* current)
0365 {
0366     activateTabPageIfNeeded(current);
0367 
0368     if (   current
0369         && !(current->flags() & Qt::ItemIsSelectable)
0370         && current->parent()
0371         && (current->parent()->flags() & Qt::ItemIsSelectable)
0372        )
0373     {
0374         d->slotSelectionChanged_enabled = false;
0375         foreach (QTreeWidgetItem *selectedItem, selectedItems()) {
0376             selectedItem->setSelected(false);
0377         }
0378         selectWidgetForItem(current->parent());
0379         setCurrentItem(current->parent());
0380         current->parent()->setSelected(true);
0381         d->slotSelectionChanged_enabled = true;
0382         return current->parent();
0383     }
0384     return current;
0385 }
0386 
0387 void WidgetTreeWidget::slotSelectionChanged()
0388 {
0389     if (!d->form || !d->slotSelectionChanged_enabled)
0390         return;
0391     const bool hadFocus = hasFocus();
0392     const QList<QTreeWidgetItem*> list( selectedItems() );
0393     d->selectWidget_enabled = false; // to avoid execution seleting form's item
0394                                     // on the tree when selectFormWidget() is called
0395     d->form->selectFormWidget();
0396     d->selectWidget_enabled = true;
0397     foreach (QTreeWidgetItem *item, list) {
0398         selectWidgetForItem(item);
0399     }
0400     tryToAlterSelection(currentItem());
0401     if (hadFocus)
0402         setFocus(); //restore focus
0403 }
0404 
0405 void WidgetTreeWidget::addItem(KFormDesigner::ObjectTreeItem *item)
0406 {
0407     WidgetTreeWidgetItem *parent = findItem(item->parent()->name());
0408     if (!parent)
0409         return;
0410 
0411     WidgetTreeWidgetItem::LoadTreeFlags flags;
0412     const KUndo2Command *topCommand = d->form->command(d->form->commandsCount() - 1);
0413     if (dynamic_cast<const InsertPageCommand*>(topCommand)) {
0414         //qDebug() << "InsertPageCommand";
0415         flags |= WidgetTreeWidgetItem::LoadTreeForAddedTabPage;
0416     } else if (dynamic_cast<const RemovePageCommand*>(topCommand)) {
0417         //qDebug() << "undoing RemovePageCommand";
0418         flags |= WidgetTreeWidgetItem::LoadTreeForAddedTabPage;
0419     }
0420     loadTree(item, parent, flags);
0421 }
0422 
0423 void WidgetTreeWidget::removeItem(KFormDesigner::ObjectTreeItem *item)
0424 {
0425     if (!item)
0426         return;
0427     const KUndo2Command *topCommand = d->form->command(d->form->commandsCount() - 1);
0428     if (dynamic_cast<const RemovePageCommand*>(topCommand)) {
0429         //qDebug() << "RemovePageCommand";
0430     }
0431     WidgetTreeWidgetItem *it = findItem(item->name());
0432 
0433     if (!it) {
0434         qWarning() << "cannot remove item with name" << item->name();
0435         return;
0436     }
0437 
0438     QTreeWidgetItem * parent = it->parent();
0439     parent->takeChild(parent->indexOfChild(it));
0440 
0441     delete it;
0442 }
0443 
0444 void WidgetTreeWidget::renameItem(const QByteArray &oldname, const QByteArray &newname)
0445 {
0446     if (findItemByFirstColumn(newname)) {
0447         qWarning() << "item with name" << newname << "already exists, cannot rename";
0448         return;
0449     }
0450     WidgetTreeWidgetItem *item = findItemByFirstColumn(oldname);
0451     if (!item)
0452         return;
0453     item->setText(0, newname);
0454 }
0455 
0456 void WidgetTreeWidget::setForm(Form *form)
0457 {
0458     d->slotSelectionChanged_enabled = false;
0459     if (d->form) {
0460         disconnect(d->form, SIGNAL(destroying()), this, SLOT(slotBeforeFormDestroyed()));
0461         disconnect(d->form, SIGNAL(selectionChanged(QWidget*,KFormDesigner::Form::WidgetSelectionFlags)),
0462             this, SLOT(selectWidget(QWidget*,KFormDesigner::Form::WidgetSelectionFlags)));
0463         disconnect(d->form, SIGNAL(childRemoved(KFormDesigner::ObjectTreeItem*)),
0464             this, SLOT(removeItem(KFormDesigner::ObjectTreeItem*)));
0465         disconnect(d->form, SIGNAL(childAdded(KFormDesigner::ObjectTreeItem*)),
0466             this, SLOT(addItem(KFormDesigner::ObjectTreeItem*)));
0467         disconnect(d->form, SIGNAL(widgetNameChanged(QByteArray,QByteArray)),
0468             this, SLOT(renameItem(QByteArray,QByteArray)));
0469 
0470     }
0471     d->form = form;
0472     clear();
0473 
0474     if (!d->form)
0475         return;
0476 
0477     connect(d->form, SIGNAL(destroying()), this, SLOT(slotBeforeFormDestroyed()));
0478     connect(d->form, SIGNAL(selectionChanged(QWidget*,KFormDesigner::Form::WidgetSelectionFlags)),
0479         this, SLOT(selectWidget(QWidget*,KFormDesigner::Form::WidgetSelectionFlags)));
0480     connect(d->form, SIGNAL(childRemoved(KFormDesigner::ObjectTreeItem*)),
0481         this, SLOT(removeItem(KFormDesigner::ObjectTreeItem*)));
0482     connect(d->form, SIGNAL(childAdded(KFormDesigner::ObjectTreeItem*)),
0483         this, SLOT(addItem(KFormDesigner::ObjectTreeItem*)));
0484     connect(d->form, SIGNAL(widgetNameChanged(QByteArray,QByteArray)),
0485         this, SLOT(renameItem(QByteArray,QByteArray)));
0486 
0487     ObjectTree *tree = d->form->objectTree();
0488     QTreeWidgetItem *root = invisibleRootItem();
0489     loadTree(tree, static_cast<WidgetTreeWidgetItem*>(root));
0490     sortItems(0, Qt::AscendingOrder);
0491 
0492     if (!form->selectedWidgets()->isEmpty())
0493         selectWidget(form->selectedWidgets()->first());
0494     else
0495         selectWidget(form->widget());
0496     d->slotSelectionChanged_enabled = true;
0497     resizeColumnToContents(0);
0498 }
0499 
0500 void WidgetTreeWidget::slotBeforeFormDestroyed()
0501 {
0502     setForm(0);
0503 }
0504 
0505 void WidgetTreeWidget::loadTree(ObjectTreeItem *item, WidgetTreeWidgetItem *parent,
0506     WidgetTreeWidgetItem::LoadTreeFlags flags)
0507 {
0508     if (!item)
0509         return;
0510 
0511     const KUndo2Command *topCommand = d->form->command(d->form->commandsCount() - 1);
0512     const RemovePageCommand* removePageCommand
0513         = dynamic_cast<const RemovePageCommand*>(topCommand);
0514     int forcedTabPageIndex;
0515     QString forcedTabPageName;
0516     if (removePageCommand) {
0517         //qDebug() << "undoing RemovePageCommand - fixing item name and index";
0518         forcedTabPageIndex = removePageCommand->pageIndex();
0519         forcedTabPageName = removePageCommand->pageName();
0520     }
0521     else
0522         forcedTabPageIndex = -1;
0523 
0524     WidgetTreeWidgetItem *treeItem = new WidgetTreeWidgetItem(parent, item, flags,
0525                                                               forcedTabPageIndex, forcedTabPageName);
0526     treeItem->setExpanded(true);
0527 
0528     ObjectTreeList *list = item->children();
0529     if (flags & WidgetTreeWidgetItem::LoadTreeForAddedTabPage)
0530         flags ^= WidgetTreeWidgetItem::LoadTreeForAddedTabPage; // this flag does not propagate to children
0531     foreach (ObjectTreeItem *titem, *list) {
0532         loadTree(titem, treeItem);
0533     }
0534 }
0535