File indexing completed on 2024-12-08 05:08:39

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "itemgroup.h"
0012 #include "icndocument.h"
0013 #include "item.h"
0014 #include "mechanicsdocument.h"
0015 #include "utils.h"
0016 
0017 #include <QTimer>
0018 #include <map>
0019 
0020 ItemGroup::ItemGroup(ItemDocument *view)
0021     : QObject(view)
0022 {
0023     m_activeItem = nullptr;
0024     b_itemsAreSameType = true;
0025     p_view = view;
0026     p_icnDocument = dynamic_cast<ICNDocument *>(p_view);
0027     p_mechanicsDocument = dynamic_cast<MechanicsDocument *>(p_view);
0028     QTimer::singleShot(0, this, SLOT(getViewPtrs()));
0029 }
0030 
0031 ItemGroup::~ItemGroup()
0032 {
0033 }
0034 
0035 void ItemGroup::getViewPtrs()
0036 {
0037     p_icnDocument = dynamic_cast<ICNDocument *>(p_view);
0038     p_mechanicsDocument = dynamic_cast<MechanicsDocument *>(p_view);
0039 }
0040 
0041 ItemList ItemGroup::items(bool excludeParentedItems) const
0042 {
0043     if (excludeParentedItems)
0044         return m_itemList;
0045 
0046     ItemList items = m_itemList;
0047     ItemList parents = m_itemList;
0048 
0049     int oldSize = items.size();
0050     do {
0051         oldSize = items.size();
0052         ItemList children;
0053 
0054         ItemList::iterator end = parents.end();
0055         for (ItemList::iterator it = parents.begin(); it != end; ++it)
0056             children += (*it)->children();
0057 
0058         end = children.end();
0059         for (ItemList::iterator it = children.begin(); it != end; ++it) {
0060             if (children.count(*it) > 1)
0061                 *it = nullptr;
0062         }
0063         children.removeAll(static_cast<Item *>(nullptr));
0064 
0065         items += children;
0066         parents = children;
0067     } while (oldSize != items.size());
0068 
0069     return items;
0070 }
0071 
0072 bool ItemGroup::itemsHaveSameDataValue(const QString &id) const
0073 {
0074     if (m_itemList.size() < 1) {
0075         return true;
0076     }
0077 
0078     if (!itemsAreSameType()) {
0079         return false;
0080     }
0081 
0082     ItemList::const_iterator it = m_itemList.begin();
0083     const ItemList::const_iterator end = m_itemList.end();
0084     QVariant firstData = (*it)->property(id)->value();
0085     for (++it; it != end; ++it) {
0086         if ((*it) && (*it)->property(id) && (*it)->property(id)->value() != firstData) {
0087             return false;
0088         }
0089     }
0090     return true;
0091 }
0092 
0093 bool ItemGroup::itemsHaveSameData() const
0094 {
0095     if (m_itemList.size() < 1) {
0096         return true;
0097     }
0098 
0099     if (!itemsAreSameType()) {
0100         return false;
0101     }
0102 
0103     VariantDataMap *variantMap = m_itemList.first()->variantMap();
0104     const VariantDataMap::const_iterator vitEnd = variantMap->end();
0105     for (VariantDataMap::const_iterator vit = variantMap->begin(); vit != vitEnd; ++vit) {
0106         if (!itemsHaveSameDataValue(vit.key())) {
0107             return false;
0108         }
0109     }
0110     return true;
0111 }
0112 
0113 bool ItemGroup::itemsHaveDefaultData() const
0114 {
0115     if (!itemsHaveSameData()) {
0116         return false;
0117     }
0118 
0119     if (m_itemList.size() < 1) {
0120         return true;
0121     }
0122 
0123     VariantDataMap *variantMap = (*m_itemList.begin())->variantMap();
0124     const VariantDataMap::const_iterator vitEnd = variantMap->end();
0125     for (VariantDataMap::const_iterator vit = variantMap->begin(); vit != vitEnd; ++vit) {
0126         if (!vit.value()->isHidden() && vit.value()->value() != vit.value()->defaultValue())
0127             return false;
0128     }
0129     return true;
0130 }
0131 
0132 void ItemGroup::registerItem(Item *item)
0133 {
0134     if (!item || m_itemList.contains(item)) {
0135         return;
0136     }
0137 
0138     m_itemList += item;
0139     updateAreSameStatus();
0140 }
0141 
0142 void ItemGroup::unregisterItem(Item *item)
0143 {
0144     if (m_itemList.removeAll(item) > 0) {
0145         updateAreSameStatus();
0146     }
0147 }
0148 
0149 void ItemGroup::updateAreSameStatus()
0150 {
0151     b_itemsAreSameType = true;
0152 
0153     if (m_itemList.size() < 2) {
0154         return;
0155     }
0156 
0157     QString activeId = (*m_itemList.begin())->id();
0158     int discardIndex = activeId.lastIndexOf("__");
0159     if (discardIndex != -1)
0160         activeId.truncate(discardIndex);
0161 
0162     const ItemList::iterator end = m_itemList.end();
0163     for (ItemList::iterator it = ++m_itemList.begin(); it != end && b_itemsAreSameType; ++it) {
0164         if (*it) {
0165             QString id = (*it)->id();
0166             discardIndex = id.lastIndexOf("__");
0167             if (discardIndex != -1)
0168                 id.truncate(discardIndex);
0169             if (id != activeId) {
0170                 b_itemsAreSameType = false;
0171             }
0172         }
0173     }
0174 }
0175 
0176 void ItemGroup::slotAlignHorizontally()
0177 {
0178     if (m_itemList.size() < 2)
0179         return;
0180 
0181     double avg_y = 0.;
0182 
0183     const ItemList::iterator end = m_itemList.end();
0184     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it)
0185         avg_y += (*it)->y();
0186 
0187     int new_y = int(avg_y / (8 * m_itemList.size())) * 8 + 4;
0188 
0189     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it)
0190         (*it)->move((*it)->x(), new_y);
0191 
0192     p_icnDocument->requestStateSave();
0193 }
0194 
0195 void ItemGroup::slotAlignVertically()
0196 {
0197     if (m_itemList.size() < 2)
0198         return;
0199 
0200     double avg_x = 0.;
0201 
0202     const ItemList::iterator end = m_itemList.end();
0203     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it)
0204         avg_x += (*it)->x();
0205 
0206     int new_x = int(avg_x / (8 * m_itemList.size())) * 8 + 4;
0207 
0208     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it)
0209         (*it)->move(new_x, (*it)->y());
0210 
0211     p_icnDocument->requestStateSave();
0212 }
0213 
0214 void ItemGroup::slotDistributeHorizontally()
0215 {
0216     if (m_itemList.size() < 2)
0217         return;
0218 
0219     // We sort the items by their horizontal position so that we can calculate
0220     // an average spacing
0221     typedef std::multimap<double, Item *> DIMap;
0222 
0223     DIMap ranked;
0224     const ItemList::iterator ilend = m_itemList.end();
0225     for (ItemList::iterator it = m_itemList.begin(); it != ilend; ++it)
0226         ranked.insert(std::make_pair((*it)->x(), *it));
0227 
0228     double avg_spacing = 0;
0229 
0230     Item *previous = nullptr;
0231     const DIMap::iterator rankedEnd = ranked.end();
0232     for (DIMap::iterator it = ranked.begin(); it != rankedEnd; ++it) {
0233         Item *item = it->second;
0234         if (previous) {
0235             double spacing = item->x() + item->offsetX() - (previous->x() + previous->width() + previous->offsetX());
0236             avg_spacing += spacing;
0237         }
0238         previous = item;
0239     }
0240 
0241     avg_spacing /= (m_itemList.size() - 1);
0242 
0243     DIMap::iterator it = ranked.begin();
0244     // Position that we are up to
0245     double at = it->second->x() + it->second->width() + it->second->offsetX();
0246     for (++it; it != rankedEnd; ++it) {
0247         Item *item = it->second;
0248         double new_x = at - item->offsetX() + avg_spacing;
0249         item->move(snapToCanvas(new_x), item->y());
0250         at = new_x + item->width() + item->offsetX();
0251     }
0252 
0253     p_icnDocument->requestStateSave();
0254 }
0255 
0256 void ItemGroup::slotDistributeVertically()
0257 {
0258     if (m_itemList.size() < 2)
0259         return;
0260 
0261     // We sort the items by their horizontal position so that we can calculate
0262     // an average spacing
0263     typedef std::multimap<double, Item *> DIMap;
0264 
0265     DIMap ranked;
0266     const ItemList::iterator ilend = m_itemList.end();
0267     for (ItemList::iterator it = m_itemList.begin(); it != ilend; ++it)
0268         ranked.insert(std::make_pair((*it)->y(), *it));
0269 
0270     double avg_spacing = 0;
0271 
0272     Item *previous = nullptr;
0273     const DIMap::iterator rankedEnd = ranked.end();
0274     for (DIMap::iterator it = ranked.begin(); it != rankedEnd; ++it) {
0275         Item *item = it->second;
0276         if (previous) {
0277             double spacing = item->y() + item->offsetY() - (previous->y() + previous->height() + previous->offsetY());
0278             avg_spacing += spacing;
0279         }
0280         previous = item;
0281     }
0282 
0283     avg_spacing /= (m_itemList.size() - 1);
0284 
0285     DIMap::iterator it = ranked.begin();
0286     // Position that we are up to
0287     double at = it->second->y() + it->second->height() + it->second->offsetY();
0288     for (++it; it != rankedEnd; ++it) {
0289         Item *item = it->second;
0290         double new_y = at - item->offsetY() + avg_spacing;
0291         item->move(item->x(), snapToCanvas(new_y));
0292         at = new_y + item->height() + item->offsetY();
0293     }
0294 
0295     p_icnDocument->requestStateSave();
0296 }
0297 
0298 #include "moc_itemgroup.cpp"