File indexing completed on 2024-05-19 12:54:39

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