File indexing completed on 2024-04-14 03:49:03

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "containermodel.h"
0007 
0008 #include "containermimedata.h"
0009 #include "vocabularymimedata.h"
0010 
0011 #include <KEduVocExpression>
0012 #include <KEduVocWordtype>
0013 
0014 #include <KLocalizedString>
0015 #include <QDebug>
0016 
0017 /** @file
0018  * Implementation of ContainerModel.
0019  * Functions to create the model from the lessons of the vocabulary document.
0020  */
0021 
0022 ContainerModel::ContainerModel(KEduVocContainer::EnumContainerType type, QObject *parent)
0023     : ReadonlyContainerModel(type, parent)
0024 {
0025     m_type = type;
0026 }
0027 
0028 QModelIndex ContainerModel::appendContainer(const QModelIndex &parent, const QString &containerName)
0029 {
0030     KEduVocContainer *parentContainer;
0031     if (parent.isValid()) {
0032         parentContainer = static_cast<KEduVocContainer *>(parent.internalPointer());
0033     } else {
0034         return QModelIndex();
0035     }
0036 
0037     beginInsertRows(parent, parentContainer->childContainerCount(), parentContainer->childContainerCount());
0038     switch (m_type) {
0039     case (KEduVocContainer::Lesson):
0040         parentContainer->appendChildContainer(new KEduVocLesson(containerName, static_cast<KEduVocLesson *>(parentContainer)));
0041         break;
0042     case (KEduVocContainer::WordType): {
0043         KEduVocWordType *parentWordType = static_cast<KEduVocWordType *>(parentContainer);
0044         KEduVocWordType *wordTypeContainer = new KEduVocWordType(containerName, parentWordType);
0045         wordTypeContainer->setWordType(parentWordType->wordType());
0046         parentContainer->appendChildContainer(wordTypeContainer);
0047         break;
0048     }
0049     default:
0050         break;
0051     }
0052     endInsertRows();
0053 
0054     return index(parentContainer->childContainerCount() - 1, 0, parent);
0055 }
0056 
0057 QVariant ContainerModel::data(const QModelIndex &index, int role) const
0058 {
0059     if (!index.isValid()) {
0060         return QVariant();
0061     }
0062 
0063     KEduVocContainer *container = static_cast<KEduVocContainer *>(index.internalPointer());
0064 
0065     switch (index.column()) {
0066     case ContainerNameColumn:
0067         if (role == Qt::DisplayRole || role == Qt::EditRole) {
0068             return container->name();
0069         }
0070 
0071         //         if (role == Qt::DecorationRole) {
0072         //             return QIcon::fromTheme("favorites");
0073         //         }
0074         if (role == Qt::TextAlignmentRole) {
0075             return Qt::AlignLeft;
0076         }
0077         break;
0078     case TotalCountColumn:
0079         if (role == Qt::DisplayRole) {
0080             return container->entryCount(KEduVocContainer::Recursive);
0081         }
0082         if (role == Qt::TextAlignmentRole) {
0083             return Qt::AlignRight;
0084         }
0085         break;
0086     default:
0087         if (role == Qt::TextAlignmentRole) {
0088             return Qt::AlignRight;
0089         }
0090     }
0091 
0092     return QVariant();
0093 }
0094 
0095 bool ContainerModel::setData(const QModelIndex &index, const QVariant &value, int role)
0096 {
0097     if (!index.isValid()) {
0098         return false;
0099     }
0100 
0101     if (index.column() == ContainerNameColumn) {
0102         KEduVocContainer *container = static_cast<KEduVocContainer *>(index.internalPointer());
0103         // rename a lesson
0104         if (role == Qt::EditRole) {
0105             container->setName(value.toString());
0106             Q_EMIT dataChanged(index, index);
0107             Q_EMIT documentModified();
0108             return true;
0109         }
0110 
0111         // checkboxes
0112         if (role == Qt::CheckStateRole) {
0113             bool newState = value.toBool();
0114             for (int i = 0; i < rowCount(index); i++) {
0115                 setData(index.model()->index(i, 0, index), newState, Qt::CheckStateRole);
0116             }
0117             container->setInPractice(newState);
0118             Q_EMIT dataChanged(index, index);
0119             Q_EMIT documentModified();
0120             return true;
0121         }
0122     }
0123     return false;
0124 }
0125 
0126 Qt::ItemFlags ContainerModel::flags(const QModelIndex &index) const
0127 {
0128     if (index.isValid()) {
0129         // the root element, not editable for now
0130         if (index.parent() == QModelIndex()) {
0131             return (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
0132         }
0133         // the name column
0134         if (index.column() == ContainerNameColumn) {
0135             return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
0136         } else { // every other element
0137             return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled);
0138         }
0139     }
0140     return Qt::ItemIsDropEnabled;
0141 }
0142 
0143 QVariant ContainerModel::headerData(int section, Qt::Orientation orientation, int role) const
0144 {
0145     // statically two columns for now
0146     if (orientation == Qt::Horizontal) {
0147         switch (section) {
0148         case ContainerNameColumn:
0149             if (role == Qt::DisplayRole) {
0150                 return i18n("Unit");
0151             }
0152             break;
0153         case TotalCountColumn:
0154             if (role == Qt::DisplayRole) {
0155                 return QVariant();
0156             }
0157             if (role == Qt::ToolTipRole) {
0158                 return i18n("Number of entries in this lesson.");
0159             }
0160             break;
0161         }
0162     }
0163     return QVariant();
0164 }
0165 
0166 int ContainerModel::columnCount(const QModelIndex &parent) const
0167 {
0168     Q_UNUSED(parent);
0169     if (!m_doc) {
0170         return FirstDataColumn;
0171     }
0172 
0173     // for now one grade per language
0174     return FirstDataColumn; // + m_doc->identifierCount();
0175 }
0176 
0177 void ContainerModel::deleteContainer(const QModelIndex &containerIndex)
0178 {
0179     KEduVocContainer *container = static_cast<KEduVocContainer *>(containerIndex.internalPointer());
0180     KEduVocContainer *parent = container->parent();
0181 
0182     if (!parent) {
0183         // never delete the root container
0184         return;
0185     }
0186 
0187     beginRemoveRows(containerIndex.parent(), containerIndex.row(), containerIndex.row());
0188     parent->deleteChildContainer(container->row());
0189     endRemoveRows();
0190 }
0191 
0192 Qt::DropActions ContainerModel::supportedDropActions() const
0193 {
0194     return Qt::MoveAction | Qt::CopyAction;
0195 }
0196 
0197 QStringList ContainerModel::mimeTypes() const
0198 {
0199     return QStringList() << QStringLiteral("text/plain");
0200 }
0201 
0202 QMimeData *ContainerModel::mimeData(const QModelIndexList &indexes) const
0203 {
0204     ContainerMimeData *mimeData = new ContainerMimeData();
0205     //      QByteArray encodedData;
0206 
0207     for (const QModelIndex &index : indexes) {
0208         mimeData->addContainer(static_cast<KEduVocContainer *>(index.internalPointer()));
0209     }
0210     mimeData->setText(QStringLiteral("Parley lesson"));
0211 
0212     //      QDataStream stream(&encodedData, QIODevice::WriteOnly);
0213     // stream << "Parley lesson";
0214     //      for (const QModelIndex &index : qAsConst(indexes)) {
0215     //          if (index.isValid()) {
0216     //              QString text = data(index, Qt::DisplayRole).toString();
0217     //              stream << text;
0218     //          }
0219     //      }
0220     // // qDebug() << "mimeData:" << encodedData;
0221     //      mimeData->setData("text/plain", encodedData);
0222     return mimeData;
0223 }
0224 
0225 bool ContainerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
0226 {
0227     Q_UNUSED(column)
0228 
0229     if (action == Qt::IgnoreAction) {
0230         return true;
0231     }
0232 
0233     // if it's internal, get the pointers
0234     const ContainerMimeData *containerData = qobject_cast<const ContainerMimeData *>(data);
0235 
0236     if (containerData) {
0237         const QList<KEduVocContainer *> containerList = containerData->containerList();
0238         for (KEduVocContainer *container : containerList) {
0239             // no way to move a word type to a lesson for now
0240             if (container->containerType() != m_type) {
0241                 return false;
0242             }
0243 
0244             if (action == Qt::MoveAction || action == Qt::CopyAction) {
0245                 qDebug() << "Move container: " << container->name();
0246                 KEduVocContainer *parentContainer = nullptr;
0247 
0248                 if (parent.isValid()) {
0249                     parentContainer = static_cast<KEduVocContainer *>(parent.internalPointer());
0250                 }
0251                 if (!parentContainer) {
0252                     // drop into root
0253                     parentContainer = rootContainer();
0254                 } else {
0255                     // make sure a container cannot be dropped into one of its child containers!
0256                     KEduVocContainer *childTest = parentContainer;
0257                     while (childTest != nullptr) {
0258                         if (childTest == container) {
0259                             qDebug() << "Cannot drop a container into one of its child containers!";
0260                             return false;
0261                         }
0262                         childTest = childTest->parent();
0263                     }
0264                 }
0265 
0266                 QModelIndex oldParent = index(container->parent());
0267 
0268                 beginRemoveRows(oldParent, container->row(), container->row());
0269                 container->parent()->removeChildContainer(container->row());
0270                 endRemoveRows();
0271 
0272                 // if we get to choose, append seems sane.
0273                 if (row < 0) {
0274                     row = parentContainer->childContainerCount();
0275                 }
0276 
0277                 // use index because we sometimes reparent to the root container instead of dropping into nowhere
0278                 beginInsertRows(index(parentContainer), row, row);
0279                 parentContainer->insertChildContainer(row, container);
0280                 endInsertRows();
0281 
0282                 return true;
0283             }
0284         }
0285     }
0286 
0287     // if it's a translation, get the pointers
0288     const VocabularyMimeData *translationData = qobject_cast<const VocabularyMimeData *>(data);
0289 
0290     if (translationData) {
0291         if (!parent.isValid()) {
0292             return false;
0293         }
0294         if (containerType() == KEduVocContainer::Lesson) {
0295             // Create a list of the entries associated with the translations being copied. This prevents duplicates if they highlighted several columns.
0296             QList<KEduVocExpression *> entries;
0297 
0298             const QList<KEduVocTranslation *> translationList = translationData->translationList();
0299             for (const auto &translation : translationList) {
0300                 if (!entries.contains(translation->entry())) {
0301                     entries << translation->entry();
0302                 }
0303             }
0304 
0305             for (KEduVocExpression *entry : qAsConst(entries)) {
0306                 static_cast<KEduVocLesson *>(parent.internalPointer())->appendEntry(new KEduVocExpression(*entry));
0307             }
0308         }
0309 
0310         if (containerType() == KEduVocContainer::WordType) {
0311             const QList<KEduVocTranslation *> translationList = translationData->translationList();
0312             for (const auto &translation : translationList) {
0313                 translation->setWordType(static_cast<KEduVocWordType *>(parent.internalPointer()));
0314             }
0315         }
0316         return false;
0317     }
0318 
0319     qDebug() << data->formats();
0320     /*
0321         if (data->hasText()) {
0322             if (action == Qt::CopyAction | action == Qt::MoveAction) {
0323                 QString name;
0324                 name = data->text();
0325                 qDebug() << "Copy lesson " << name;
0326 
0327                 appendLesson(parent, name);
0328                 return true;
0329             }
0330         }*/
0331 
0332     return false;
0333 }
0334 
0335 Qt::DropActions ContainerModel::supportedDragActions() const
0336 {
0337     return (Qt::CopyAction | Qt::MoveAction);
0338 }
0339 
0340 /*
0341 bool ContainerModel::removeRows(int row, int count, const QModelIndex & parent)
0342 {
0343 // BIG FAT WARNING this code works, but it gets called by the drag and drop implementation automatically, so either this gets used or the other (dropMimeData)
0344 to remove the containers. If both are active, containers get deleted without warning or visible signs.
0345 
0346     KEduVocContainer* parentContainer;
0347     if (!parent.internalPointer()) {
0348         parentContainer = m_container;
0349     } else {
0350         parentContainer = static_cast<KEduVocContainer*>(parent.internalPointer());
0351     }
0352     qDebug() << "removeRows from " << parentContainer->name() << " row " << row << "count" << count;
0353 
0354     beginRemoveRows ( parent, row, row+count );
0355     for (int i = 0; i<count; i++) {
0356         parentContainer->removeChildContainer(row);
0357     }
0358     endRemoveRows();
0359 
0360     return true;
0361 }
0362 
0363 */
0364 
0365 #include "moc_containermodel.cpp"