File indexing completed on 2024-04-28 05:48:39

0001 /***************************************************************************
0002  *   This file is part of Kate build plugin                                *
0003  *   SPDX-FileCopyrightText: 2014 Kåre Särs <kare.sars@iki.fi>             *
0004  *                                                                         *
0005  *   SPDX-License-Identifier: LGPL-2.0-or-later                            *
0006  ***************************************************************************/
0007 
0008 #include "TargetModel.h"
0009 #include <KLocalizedString>
0010 #include <QDebug>
0011 #include <QTimer>
0012 
0013 namespace
0014 {
0015 struct NodeInfo {
0016     int rootRow = -1;
0017     int targetSetRow = -1;
0018     int commandRow = -1;
0019 
0020     bool isCommand() const
0021     {
0022         return rootRow != -1 && targetSetRow != -1 && commandRow != -1;
0023     }
0024 
0025     bool isTargetSet() const
0026     {
0027         return rootRow != -1 && targetSetRow != -1 && commandRow == -1;
0028     }
0029 
0030     bool isRoot() const
0031     {
0032         return rootRow != -1 && targetSetRow == -1 && commandRow == -1;
0033     }
0034 
0035     bool isValid() const
0036     {
0037         return rootRow != -1;
0038     }
0039 };
0040 }
0041 
0042 static QDebug operator<<(QDebug debug, const NodeInfo &node)
0043 {
0044     QDebugStateSaver saver(debug);
0045     debug << "Node:" << node.rootRow << node.targetSetRow << node.commandRow;
0046     return debug;
0047 }
0048 
0049 TargetModel::TargetSet::TargetSet(const QString &_name, const QString &_dir)
0050     : name(_name)
0051     , workDir(_dir)
0052 {
0053 }
0054 
0055 /**
0056  * This is the logical structure of the nodes in the model.
0057  * 1) In the root we have Session and/or Project
0058  * 2) Under these we have the TargetSets (working directory)
0059  * 3) Under the TargetSets we have the Targets (commands to execute and/or run commands)
0060  *
0061  * --- Session Targets
0062  *   - TargetSet
0063  *      - Command, Run
0064  *      - ...
0065  *   - TargetSet
0066  *      - Command, Run
0067  *      - ...
0068  *
0069  * --- Project Targets
0070  *   - TargetSet
0071  *      - Command, Run
0072  *      - ...
0073  *   - TargetSet
0074  *      - Command, Run
0075  *      - ...
0076  *
0077  *
0078  * How to interpret QModelIndex::internalId:
0079  * if internalId == InvalidIndex -> This is a Root element
0080  * else if internalId & TargetSetRowMask == TargetSetRowMask -> This is a TargetSet Node
0081  * else this is a command node
0082  *  (internalId == "parent rows")
0083  *
0084  * column 0 is command name, target-set name or Session/Project
0085  * column 1 is the command or working directory
0086  * column 2 is the run command
0087  */
0088 
0089 // Topmost bit is used for RootRow 0 or 1
0090 static constexpr int RootRowShift = sizeof(quintptr) * 8 - 1;
0091 
0092 // One empty bit is reserved between RootRow and TargetSetRow
0093 static constexpr quintptr TargetSetRowMask = ~((quintptr)3 << (RootRowShift - 1));
0094 
0095 /** This function converts the internalId to root-row, which can be either 0 or 1 as we only can have two of them. '
0096  * @return 0 or 1 if successful or -1 if not a valid internalId
0097  */
0098 static int idToRootRow(quintptr internalId)
0099 {
0100     if (internalId == TargetModel::InvalidIndex) {
0101         return -1;
0102     }
0103     return internalId >> RootRowShift;
0104 }
0105 
0106 static int idToTargetSetRow(quintptr internalId)
0107 {
0108     if (internalId == TargetModel::InvalidIndex) {
0109         return -1;
0110     }
0111 
0112     if ((internalId & TargetSetRowMask) == TargetSetRowMask) {
0113         return -1;
0114     }
0115     return internalId &= TargetSetRowMask;
0116 }
0117 
0118 static quintptr toInternalId(int rootRow, int targetSetRow)
0119 {
0120     if (rootRow < 0) {
0121         return TargetModel::InvalidIndex;
0122     }
0123 
0124     if (targetSetRow < 0) {
0125         return TargetSetRowMask + ((quintptr)rootRow << RootRowShift);
0126     }
0127 
0128     return ((quintptr)targetSetRow & TargetSetRowMask) + ((quintptr)rootRow << RootRowShift);
0129 }
0130 
0131 static NodeInfo modelToNodeInfo(const QModelIndex &itemIndex)
0132 {
0133     NodeInfo idx;
0134     if (!itemIndex.isValid()) {
0135         return idx;
0136     }
0137 
0138     if (itemIndex.internalId() == TargetModel::InvalidIndex) {
0139         // This is a root node
0140         idx.rootRow = itemIndex.row();
0141         return idx;
0142     }
0143 
0144     int rootRow = idToRootRow(itemIndex.internalId());
0145     int targetSetRow = idToTargetSetRow(itemIndex.internalId());
0146     if (rootRow != -1 && targetSetRow == -1) {
0147         // This is a TargetSet node
0148         idx.rootRow = rootRow;
0149         idx.targetSetRow = itemIndex.row();
0150     } //
0151     else if (rootRow != -1 && targetSetRow != -1) {
0152         // This is a Command node
0153         idx.rootRow = rootRow;
0154         idx.targetSetRow = targetSetRow;
0155         idx.commandRow = itemIndex.row();
0156     }
0157     return idx;
0158 }
0159 
0160 static bool nodeExists(const QList<TargetModel::RootNode> &rootNodes, const NodeInfo &node)
0161 {
0162     if (!node.isValid()) {
0163         return false;
0164     }
0165 
0166     if (node.rootRow < 0 || node.rootRow >= rootNodes.size()) {
0167         return false;
0168     }
0169 
0170     if (node.isRoot()) {
0171         return true;
0172     }
0173 
0174     const QList<TargetModel::TargetSet> &targets = rootNodes[node.rootRow].targetSets;
0175     if (node.targetSetRow >= targets.size()) {
0176         return false;
0177     }
0178 
0179     if (node.isTargetSet()) {
0180         return true;
0181     }
0182 
0183     const QList<TargetModel::Command> &commands = targets[node.targetSetRow].commands;
0184     if (node.commandRow >= commands.size()) {
0185         qWarning() << "Command row out of bounds" << node;
0186         return false;
0187     }
0188     // The node is valid, not root and not a target-set and command-row is valid
0189     return true;
0190 }
0191 
0192 TargetModel::TargetModel(QObject *parent)
0193     : QAbstractItemModel(parent)
0194 {
0195     m_rootNodes.append(RootNode());
0196     m_rootNodes.append(RootNode());
0197     // By default the project branch is second
0198     m_rootNodes[1].isProject = true;
0199 }
0200 TargetModel::~TargetModel()
0201 {
0202 }
0203 
0204 void TargetModel::clear(bool setSessionFirst)
0205 {
0206     beginResetModel();
0207     m_rootNodes.clear();
0208     m_rootNodes.append(RootNode());
0209     m_rootNodes.append(RootNode());
0210     m_rootNodes[setSessionFirst ? 1 : 0].isProject = true;
0211     endResetModel();
0212 }
0213 
0214 QModelIndex TargetModel::sessionRootIndex() const
0215 {
0216     for (int i = 0; i < m_rootNodes.size(); ++i) {
0217         if (!m_rootNodes[i].isProject) {
0218             return index(i, 0);
0219         }
0220     }
0221     return QModelIndex();
0222 }
0223 
0224 QModelIndex TargetModel::projectRootIndex() const
0225 {
0226     for (int i = 0; i < m_rootNodes.size(); ++i) {
0227         if (m_rootNodes[i].isProject) {
0228             return index(i, 0);
0229         }
0230     }
0231     return QModelIndex();
0232 }
0233 
0234 QModelIndex TargetModel::insertTargetSetAfter(const QModelIndex &beforeIndex, const QString &setName, const QString &workDir)
0235 {
0236     // qDebug() << "Inserting TargetSet after:" << beforeIndex << setName <<workDir;
0237     NodeInfo bNode = modelToNodeInfo(beforeIndex);
0238     if (!nodeExists(m_rootNodes, bNode)) {
0239         // Add the new target-set to the end of the first root node (creating the root if needed)
0240         if (m_rootNodes.isEmpty()) {
0241             beginInsertRows(QModelIndex(), 0, 0);
0242             m_rootNodes.append(RootNode());
0243             endInsertRows();
0244         }
0245         bNode.rootRow = 0;
0246         bNode.targetSetRow = m_rootNodes[0].targetSets.size() - 1;
0247     }
0248 
0249     if (bNode.isRoot()) {
0250         bNode.targetSetRow = m_rootNodes[bNode.rootRow].targetSets.size() - 1;
0251     }
0252 
0253     QList<TargetSet> &targetSets = m_rootNodes[bNode.rootRow].targetSets;
0254 
0255     // Make the name unique
0256     QString newName = setName;
0257     for (int i = 0; i < targetSets.count(); i++) {
0258         if (targetSets[i].name == newName) {
0259             newName += QStringLiteral("+");
0260             i = -1;
0261         }
0262     }
0263     bNode.targetSetRow++;
0264 
0265     beginInsertRows(index(bNode.rootRow, 0), bNode.targetSetRow, bNode.targetSetRow);
0266     TargetModel::TargetSet targetSet(newName, workDir);
0267     targetSets.insert(bNode.targetSetRow, targetSet);
0268     endInsertRows();
0269     return index(bNode.targetSetRow, 0, index(bNode.rootRow, 0));
0270 }
0271 
0272 QModelIndex TargetModel::addCommandAfter(const QModelIndex &beforeIndex, const QString &cmdName, const QString &buildCmd, const QString &runCmd)
0273 {
0274     // qDebug() << "addCommandAfter" << beforeIndex << cmdName;
0275     NodeInfo bNode = modelToNodeInfo(beforeIndex);
0276     if (!nodeExists(m_rootNodes, bNode)) {
0277         // Add the new command to the end of the first target-set of the first root node (creating the root and target-set if needed)
0278         if (m_rootNodes.isEmpty()) {
0279             beginInsertRows(QModelIndex(), 0, 0);
0280             m_rootNodes.append(RootNode());
0281             endInsertRows();
0282         }
0283         if (m_rootNodes[0].targetSets.isEmpty()) {
0284             beginInsertRows(index(0, 0), 0, 0);
0285             m_rootNodes[0].targetSets.append(TargetSet(i18n("Target Set"), QString()));
0286             endInsertRows();
0287         }
0288         bNode.rootRow = 0;
0289         bNode.targetSetRow = 0;
0290         bNode.commandRow = m_rootNodes[0].targetSets[0].commands.size() - 1;
0291     }
0292 
0293     if (bNode.isRoot()) {
0294         // Add the new command to the first target-set of this root node (creating the targetset if needed)
0295         if (m_rootNodes[bNode.rootRow].targetSets.isEmpty()) {
0296             beginInsertRows(index(bNode.rootRow, 0), 0, 0);
0297             m_rootNodes[bNode.rootRow].targetSets.append(TargetSet(i18n("Target Set"), QString()));
0298             endInsertRows();
0299         }
0300         bNode.targetSetRow = 0;
0301         bNode.commandRow = m_rootNodes[bNode.rootRow].targetSets[0].commands.size() - 1;
0302     }
0303 
0304     if (bNode.isTargetSet()) {
0305         bNode.commandRow = m_rootNodes[bNode.rootRow].targetSets[bNode.targetSetRow].commands.size() - 1;
0306     }
0307 
0308     // Now we have the place to insert the new command
0309     QList<Command> &commands = m_rootNodes[bNode.rootRow].targetSets[bNode.targetSetRow].commands;
0310     // make the name unique
0311     QString newName = cmdName;
0312     for (int i = 0; i < commands.count(); ++i) {
0313         if (commands[i].name == newName) {
0314             newName += QStringLiteral("+");
0315             i = -1;
0316         }
0317     }
0318 
0319     // it is the row after beforeIndex, where we want to insert the command
0320     bNode.commandRow++;
0321 
0322     QModelIndex targetSetIndex = index(bNode.targetSetRow, 0, index(bNode.rootRow, 0));
0323     beginInsertRows(targetSetIndex, bNode.commandRow, bNode.commandRow);
0324     commands.insert(bNode.commandRow, {newName, buildCmd, runCmd});
0325     endInsertRows();
0326     return index(bNode.commandRow, 0, targetSetIndex);
0327 }
0328 
0329 QModelIndex TargetModel::copyTargetOrSet(const QModelIndex &copyIndex)
0330 {
0331     NodeInfo cpNode = modelToNodeInfo(copyIndex);
0332     if (!nodeExists(m_rootNodes, cpNode)) {
0333         return QModelIndex();
0334     }
0335 
0336     if (cpNode.isRoot()) {
0337         return QModelIndex();
0338     }
0339 
0340     QList<TargetSet> &targetSets = m_rootNodes[cpNode.rootRow].targetSets;
0341     QModelIndex rootIndex = index(cpNode.rootRow, 0);
0342     if (cpNode.isTargetSet()) {
0343         TargetSet targetSetCopy = targetSets[cpNode.targetSetRow];
0344         // Make the name unique
0345         QString newName = targetSetCopy.name;
0346         for (int i = 0; i < targetSets.size(); ++i) {
0347             if (targetSets[i].name == newName) {
0348                 newName += QStringLiteral("+");
0349                 i = -1;
0350             }
0351         }
0352         targetSetCopy.name = newName;
0353         beginInsertRows(rootIndex, cpNode.targetSetRow + 1, cpNode.targetSetRow + 1);
0354         targetSets.insert(cpNode.targetSetRow + 1, targetSetCopy);
0355         endInsertRows();
0356         return index(cpNode.targetSetRow + 1, 0, rootIndex);
0357     }
0358 
0359     // This is a command-row
0360     QList<Command> &commands = targetSets[cpNode.targetSetRow].commands;
0361     Command commandCopy = commands[cpNode.commandRow];
0362     QString newName = commandCopy.name;
0363     for (int i = 0; i < commands.size(); i++) {
0364         if (commands[i].name == newName) {
0365             newName += QStringLiteral("+");
0366             i = -1;
0367         }
0368     }
0369     commandCopy.name = newName;
0370     QModelIndex tgSetIndex = index(cpNode.targetSetRow, 0, rootIndex);
0371     beginInsertRows(tgSetIndex, cpNode.commandRow + 1, cpNode.commandRow + 1);
0372     commands.insert(cpNode.commandRow + 1, commandCopy);
0373     endInsertRows();
0374     return index(cpNode.commandRow + 1, 0, tgSetIndex);
0375 }
0376 
0377 void TargetModel::deleteItem(const QModelIndex &itemIndex)
0378 {
0379     if (!itemIndex.isValid()) {
0380         return;
0381     }
0382 
0383     NodeInfo node = modelToNodeInfo(itemIndex);
0384     if (!nodeExists(m_rootNodes, node)) {
0385         qDebug() << "Node does not exist:" << node;
0386         return;
0387     }
0388 
0389     if (node.isRoot()) {
0390         beginRemoveRows(itemIndex, 0, m_rootNodes[node.rootRow].targetSets.size() - 1);
0391         m_rootNodes[node.rootRow].targetSets.clear();
0392         endRemoveRows();
0393     } else if (node.isTargetSet()) {
0394         beginRemoveRows(itemIndex.parent(), itemIndex.row(), itemIndex.row());
0395         m_rootNodes[node.rootRow].targetSets.removeAt(node.targetSetRow);
0396         endRemoveRows();
0397     } else {
0398         beginRemoveRows(itemIndex.parent(), itemIndex.row(), itemIndex.row());
0399         m_rootNodes[node.rootRow].targetSets[node.targetSetRow].commands.removeAt(node.commandRow);
0400         endRemoveRows();
0401     }
0402 }
0403 
0404 void TargetModel::deleteProjectTargerts()
0405 {
0406     for (int i = 0; i < m_rootNodes.count(); ++i) {
0407         if (m_rootNodes[i].isProject && m_rootNodes[i].targetSets.count() > 0) {
0408             beginRemoveRows(index(i, 0), 0, m_rootNodes[i].targetSets.count() - 1);
0409             m_rootNodes[i].targetSets.clear();
0410             endRemoveRows();
0411             return;
0412         }
0413     }
0414 }
0415 
0416 void TargetModel::moveRowUp(const QModelIndex &itemIndex)
0417 {
0418     if (!itemIndex.isValid()) {
0419         return;
0420     }
0421     NodeInfo node = modelToNodeInfo(itemIndex);
0422     if (!nodeExists(m_rootNodes, node)) {
0423         qDebug() << "Node does not exist:" << node;
0424         return;
0425     }
0426     int row = itemIndex.row();
0427     if (row == 0) {
0428         return; // This is valid for all the three cases
0429     }
0430 
0431     QModelIndex parent = itemIndex.parent(); // This parent is valid for all the cases
0432 
0433     if (node.isRoot()) {
0434         beginMoveRows(parent, row, row, parent, row - 1);
0435         m_rootNodes.move(row, row - 1);
0436         endMoveRows();
0437         return;
0438     }
0439 
0440     QList<TargetSet> &targetSets = m_rootNodes[node.rootRow].targetSets;
0441     if (node.isTargetSet()) {
0442         beginMoveRows(parent, row, row, parent, row - 1);
0443         targetSets.move(row, row - 1);
0444         endMoveRows();
0445         return;
0446     }
0447 
0448     // It is a command-row
0449     QList<Command> &commands = targetSets[node.targetSetRow].commands;
0450     beginMoveRows(parent, row, row, parent, row - 1);
0451     commands.move(row, row - 1);
0452     endMoveRows();
0453 }
0454 
0455 void TargetModel::moveRowDown(const QModelIndex &itemIndex)
0456 {
0457     if (!itemIndex.isValid()) {
0458         return;
0459     }
0460     NodeInfo node = modelToNodeInfo(itemIndex);
0461     if (!nodeExists(m_rootNodes, node)) {
0462         qDebug() << "Node does not exist:" << node;
0463         return;
0464     }
0465 
0466     // These are valid for all the row types
0467     int row = itemIndex.row();
0468     QModelIndex parent = itemIndex.parent();
0469 
0470     if (node.isRoot()) {
0471         if (row >= m_rootNodes.size() - 1) {
0472             return;
0473         }
0474         beginMoveRows(parent, row, row, parent, row + 2);
0475         m_rootNodes.move(row, row + 1);
0476         endMoveRows();
0477         return;
0478     }
0479 
0480     QList<TargetSet> &targetSets = m_rootNodes[node.rootRow].targetSets;
0481     if (node.isTargetSet()) {
0482         beginMoveRows(parent, row, row, parent, row + 2);
0483         targetSets.move(row, row + 1);
0484         endMoveRows();
0485         return;
0486     }
0487 
0488     // It is a command-row
0489     QList<Command> &commands = targetSets[node.targetSetRow].commands;
0490     beginMoveRows(parent, row, row, parent, row + 2);
0491     commands.move(row, row + 1);
0492     endMoveRows();
0493 }
0494 
0495 const QList<TargetModel::TargetSet> TargetModel::sessionTargetSets() const
0496 {
0497     for (int i = 0; i < m_rootNodes.size(); ++i) {
0498         if (m_rootNodes[i].isProject == false) {
0499             return m_rootNodes[i].targetSets;
0500         }
0501     }
0502     return QList<TargetModel::TargetSet>();
0503 }
0504 
0505 const QList<TargetModel::TargetSet> TargetModel::projectTargetSets() const
0506 {
0507     for (int i = 0; i < m_rootNodes.size(); ++i) {
0508         if (m_rootNodes[i].isProject == true) {
0509             return m_rootNodes[i].targetSets;
0510         }
0511     }
0512     return QList<TargetModel::TargetSet>();
0513 }
0514 
0515 QVariant TargetModel::data(const QModelIndex &index, int role) const
0516 {
0517     if (!index.isValid()) {
0518         qWarning() << "Invalid index" << index;
0519         return QVariant();
0520     }
0521 
0522     NodeInfo node = modelToNodeInfo(index);
0523     if (!nodeExists(m_rootNodes, node)) {
0524         qDebug() << "Node does not exist:" << node;
0525         return QVariant();
0526     }
0527 
0528     if (node.isRoot()) {
0529         if ((role == Qt::DisplayRole || role == Qt::ToolTipRole) && index.column() == 0) {
0530             return m_rootNodes[node.rootRow].isProject ? i18n("Project") : i18n("Session");
0531         } else if (role == RowTypeRole) {
0532             return RowType::RootRow;
0533         } else if (role == IsProjectTargetRole) {
0534             return m_rootNodes[node.rootRow].isProject;
0535         }
0536         return QVariant();
0537     }
0538 
0539     // This is either a TargetSet or a Command
0540     const TargetSet &targetSet = m_rootNodes[node.rootRow].targetSets[node.targetSetRow];
0541 
0542     if (node.isTargetSet()) {
0543         // This is a TargetSet node
0544         switch (role) {
0545         case Qt::DisplayRole:
0546         case Qt::EditRole:
0547         case Qt::ToolTipRole:
0548             switch (index.column()) {
0549             case 0:
0550                 return targetSet.name;
0551             case 1:
0552                 return targetSet.workDir;
0553             }
0554             break;
0555         case CommandRole:
0556             if (targetSet.commands.isEmpty()) {
0557                 return QVariant();
0558             }
0559             return targetSet.commands[0].buildCmd;
0560         case CommandNameRole:
0561             if (targetSet.commands.isEmpty()) {
0562                 return QVariant();
0563             }
0564             return targetSet.commands[0].name;
0565         case WorkDirRole:
0566             return targetSet.workDir.isEmpty() ? QString() : targetSet.workDir.split(QLatin1Char(';')).first();
0567         case SearchPathsRole:
0568             return targetSet.workDir.split(QLatin1Char(';'));
0569         case TargetSetNameRole:
0570             return targetSet.name;
0571         case RowTypeRole:
0572             return TargetSetRow;
0573         case IsProjectTargetRole:
0574             return m_rootNodes[node.rootRow].isProject;
0575         }
0576     }
0577 
0578     if (node.isCommand()) {
0579         const Command &command = targetSet.commands[node.commandRow];
0580         switch (role) {
0581         case Qt::DisplayRole:
0582         case Qt::EditRole:
0583         case Qt::ToolTipRole:
0584             switch (index.column()) {
0585             case 0:
0586                 return command.name;
0587             case 1:
0588                 return command.buildCmd;
0589             case 2:
0590                 return command.runCmd;
0591             }
0592             break;
0593         case CommandRole:
0594             return command.buildCmd;
0595         case CommandNameRole:
0596             return command.name;
0597         case WorkDirRole:
0598             return targetSet.workDir.isEmpty() ? QString() : targetSet.workDir.split(QLatin1Char(';')).first();
0599         case SearchPathsRole:
0600             return targetSet.workDir.split(QLatin1Char(';'));
0601         case TargetSetNameRole:
0602             return targetSet.name;
0603         case RowTypeRole:
0604             return CommandRow;
0605         case IsProjectTargetRole:
0606             return m_rootNodes[node.rootRow].isProject;
0607         }
0608     }
0609 
0610     return QVariant();
0611 }
0612 
0613 QVariant TargetModel::headerData(int section, Qt::Orientation orientation, int role) const
0614 {
0615     if (role != Qt::DisplayRole) {
0616         return QVariant();
0617     }
0618 
0619     if (orientation != Qt::Horizontal) {
0620         return QVariant();
0621     }
0622 
0623     if (section == 0) {
0624         return i18n("Command/Target-set Name");
0625     }
0626     if (section == 1) {
0627         return i18n("Working Directory / Command");
0628     }
0629     if (section == 2) {
0630         return i18n("Run Command");
0631     }
0632     return QVariant();
0633 }
0634 
0635 bool TargetModel::setData(const QModelIndex &itemIndex, const QVariant &value, int role)
0636 {
0637     if (role != Qt::EditRole) {
0638         return false;
0639     }
0640     if (!itemIndex.isValid()) {
0641         qWarning() << "Invalid index";
0642         return false;
0643     }
0644 
0645     NodeInfo node = modelToNodeInfo(itemIndex);
0646     if (!nodeExists(m_rootNodes, node)) {
0647         qDebug() << "Node does not exist:" << node;
0648         return false;
0649     }
0650 
0651     if (node.isRoot()) {
0652         return false;
0653     }
0654 
0655     // This is either a TargetSet or a Command
0656     TargetSet &targetSet = m_rootNodes[node.rootRow].targetSets[node.targetSetRow];
0657 
0658     bool editDone = false;
0659     if (node.isTargetSet()) {
0660         switch (itemIndex.column()) {
0661         case 0:
0662             targetSet.name = value.toString();
0663             editDone = true;
0664             break;
0665         case 1:
0666             targetSet.workDir = value.toString();
0667             editDone = true;
0668             break;
0669         }
0670     } else {
0671         switch (itemIndex.column()) {
0672         case 0:
0673             targetSet.commands[node.commandRow].name = value.toString();
0674             editDone = true;
0675             break;
0676         case 1:
0677             targetSet.commands[node.commandRow].buildCmd = value.toString();
0678             editDone = true;
0679             break;
0680         case 2:
0681             targetSet.commands[node.commandRow].runCmd = value.toString();
0682             editDone = true;
0683             break;
0684         }
0685     }
0686     if (editDone) {
0687         Q_EMIT dataChanged(itemIndex, itemIndex);
0688         if (m_rootNodes[node.rootRow].isProject) {
0689             Q_EMIT projectTargetChanged();
0690         }
0691         return true;
0692     }
0693     return false;
0694 }
0695 
0696 Qt::ItemFlags TargetModel::flags(const QModelIndex &itemIndex) const
0697 {
0698     if (!itemIndex.isValid()) {
0699         return Qt::NoItemFlags;
0700     }
0701 
0702     NodeInfo node = modelToNodeInfo(itemIndex);
0703     if (!nodeExists(m_rootNodes, node)) {
0704         return Qt::NoItemFlags;
0705     }
0706     if (node.isRoot()) {
0707         return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0708     }
0709 
0710     // run command column for target set row
0711     if (itemIndex.column() == 2 && node.isTargetSet()) {
0712         return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0713     }
0714 
0715     return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0716 }
0717 
0718 int TargetModel::rowCount(const QModelIndex &parent) const
0719 {
0720     if (!parent.isValid()) {
0721         // Invalid index -> root
0722         return m_rootNodes.size();
0723     }
0724 
0725     NodeInfo node = modelToNodeInfo(parent);
0726     if (!nodeExists(m_rootNodes, node)) {
0727         // uncomment for debugging
0728         // qDebug() << "Node does not exist:" << node << parent;
0729         return 0;
0730     }
0731 
0732     if (parent.column() != 0) {
0733         // Only first column has children
0734         return 0;
0735     }
0736 
0737     if (node.isRoot()) {
0738         return m_rootNodes[node.rootRow].targetSets.size();
0739     }
0740 
0741     if (node.isTargetSet()) {
0742         return m_rootNodes[node.rootRow].targetSets[node.targetSetRow].commands.size();
0743     }
0744 
0745     // This is a command node -> no children
0746     return 0;
0747 }
0748 
0749 int TargetModel::columnCount(const QModelIndex &) const
0750 {
0751     return 3;
0752 }
0753 
0754 QModelIndex TargetModel::index(int row, int column, const QModelIndex &parent) const
0755 {
0756     if (row < 0) {
0757         return QModelIndex();
0758     }
0759 
0760     if (!parent.isValid()) {
0761         // RootRow Item (Session/Project)
0762         if (row >= m_rootNodes.size()) {
0763             return QModelIndex();
0764         }
0765         return createIndex(row, column, InvalidIndex);
0766     }
0767 
0768     if (parent.column() != 0) {
0769         // Only column 0 can have children.
0770         return QModelIndex();
0771     }
0772 
0773     if (parent.internalId() == InvalidIndex) {
0774         // TargetSet node
0775         int rootRow = parent.row();
0776         if (rootRow >= m_rootNodes.size() || row >= m_rootNodes.at(rootRow).targetSets.size()) {
0777             return QModelIndex();
0778         }
0779         return createIndex(row, column, toInternalId(rootRow, -1));
0780     }
0781 
0782     // This is a command node
0783     int rootRow = idToRootRow(parent.internalId());
0784     int targetSetRow = parent.row();
0785     if (rootRow >= m_rootNodes.size() || targetSetRow >= m_rootNodes.at(rootRow).targetSets.size()) {
0786         return QModelIndex();
0787     }
0788     const TargetSet &tgSet = m_rootNodes.at(rootRow).targetSets.at(targetSetRow);
0789     if (row >= tgSet.commands.size()) {
0790         return QModelIndex();
0791     }
0792     return createIndex(row, column, toInternalId(rootRow, targetSetRow));
0793 }
0794 
0795 QModelIndex TargetModel::parent(const QModelIndex &child) const
0796 {
0797     if (!child.isValid()) {
0798         return QModelIndex();
0799     }
0800 
0801     if (child.internalId() == InvalidIndex) {
0802         // child is a RootRow node -> invalid parent
0803         return QModelIndex();
0804     }
0805 
0806     int rootRow = idToRootRow(child.internalId());
0807     int targetSetRow = idToTargetSetRow(child.internalId());
0808 
0809     if (targetSetRow == -1) {
0810         // child is a TargetSetNode
0811         return createIndex(rootRow, 0, InvalidIndex);
0812     }
0813 
0814     // child is a command node
0815     return createIndex(targetSetRow, 0, toInternalId(rootRow, -1));
0816 }
0817 
0818 #include "moc_TargetModel.cpp"