File indexing completed on 2024-04-21 16:30:18

0001 /***************************************************************************
0002  *   Copyright (C) 2008-2009 by Pino Toscano <pino@kde.org>                *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  ***************************************************************************/
0009 
0010 #include "alternativemodels.h"
0011 #include "altparser.h"
0012 
0013 #include <QComboBox>
0014 #include <QFile>
0015 #include <QFont>
0016 #include <QList>
0017 
0018 #include <kiconloader.h>
0019 #include <klocalizedstring.h>
0020 #include <kprocess.h>
0021 
0022 #include <algorithm>
0023 
0024 #include <config-kalternatives.h>
0025 #include <kalternatives_debug.h>
0026 
0027 enum ItemChangeType
0028 {
0029     SelectionItemChange = 1,
0030     AltNumItemChange = 2,
0031     ModeItemChange = 4
0032 };
0033 Q_DECLARE_FLAGS(ItemChanges, ItemChangeType)
0034 Q_DECLARE_OPERATORS_FOR_FLAGS(ItemChanges)
0035 
0036 namespace
0037 {
0038 
0039 struct AltNode
0040 {
0041     enum { Type = 0 };
0042 
0043     AltNode(AltNode *pp, int t)
0044         : parent(pp), type(t) {}
0045     virtual ~AltNode()
0046         {}
0047 
0048     virtual QList<AltNode*> children() const
0049         { return QList<AltNode*>(); }
0050     virtual int childCount() const
0051         { return 0; }
0052 
0053     AltNode *parent;
0054     int type : 3;
0055 };
0056 
0057 struct AltAlternativeNode;
0058 struct AltItemNode;
0059 
0060 struct AltRootNode : public AltNode
0061 {
0062     enum { Type = 1 };
0063 
0064     AltRootNode()
0065         : AltNode(Q_NULLPTR, Type) {}
0066     ~AltRootNode() Q_DECL_OVERRIDE
0067         { qDeleteAll(m_children); }
0068 
0069     QList<AltNode*> children() const Q_DECL_OVERRIDE
0070         {
0071             QList<AltNode*> c;
0072             std::copy(m_children.begin(), m_children.end(), std::back_inserter(c));
0073             return c;
0074         }
0075     int childCount() const Q_DECL_OVERRIDE
0076         { return m_children.count(); }
0077 
0078     QList<AltItemNode*> m_children;
0079 };
0080 
0081 struct AltItemNode : public AltNode
0082 {
0083     enum { Type = 2 };
0084 
0085     AltItemNode(Item *i, AltRootNode *p)
0086         : AltNode(p, Type), item(i)
0087         , changed(false), nbrAltChanged(false), modeChanged(false)
0088     {}
0089     ~AltItemNode() Q_DECL_OVERRIDE
0090         { qDeleteAll(m_children); }
0091 
0092     QList<AltNode*> children() const Q_DECL_OVERRIDE
0093         {
0094             QList<AltNode*> c;
0095             std::copy(m_children.begin(), m_children.end(), std::back_inserter(c));
0096             return c;
0097         }
0098     int childCount() const Q_DECL_OVERRIDE
0099         { return m_children.count(); }
0100 
0101     Item *item;
0102     QList<AltAlternativeNode*> m_children;
0103     bool changed : 1;
0104     bool nbrAltChanged : 1;
0105     bool modeChanged : 1;
0106 };
0107 
0108 struct AltAlternativeNode : public AltNode
0109 {
0110     enum { Type = 3 };
0111 
0112     AltAlternativeNode(Alternative *a, AltItemNode *p)
0113         : AltNode(p, Type), alternative(a), selected(false)
0114     {}
0115 
0116     Alternative *alternative;
0117     bool selected : 1;
0118 };
0119 
0120 template <class T>
0121 T* altnode_cast(AltNode *n)
0122 {
0123     return n->type > 0 && n->type == T::Type ? static_cast<T*>(n) : Q_NULLPTR;
0124 }
0125 
0126 template <>
0127 AltNode* altnode_cast(AltNode *n)
0128 {
0129     return n;
0130 }
0131 
0132 }
0133 
0134 class AlternativesBaseModelPrivate
0135 {
0136 public:
0137     AlternativesBaseModelPrivate();
0138     virtual ~AlternativesBaseModelPrivate();
0139 
0140     Q_DECLARE_PUBLIC(AlternativesBaseModel)
0141 
0142     virtual void load() = 0;
0143     virtual AltNode* root() const = 0;
0144     QModelIndex indexForItem(AltNode *n, int col) const;
0145 
0146     AlternativesBaseModel *q_ptr;
0147 };
0148 
0149 AlternativesBaseModelPrivate::AlternativesBaseModelPrivate()
0150     : q_ptr(Q_NULLPTR)
0151 {
0152 }
0153 
0154 AlternativesBaseModelPrivate::~AlternativesBaseModelPrivate()
0155 {
0156 }
0157 
0158 QModelIndex AlternativesBaseModelPrivate::indexForItem(AltNode *n, int col) const
0159 {
0160     if (n->parent)
0161     {
0162         const QList<AltNode*> children = n->parent->children();
0163         const int id = children.indexOf(n);
0164         if (id >= 0 && id < children.count())
0165            return q_ptr->createIndex(id, col, n);
0166     }
0167     return QModelIndex();
0168 }
0169 
0170 
0171 AlternativesBaseModel::AlternativesBaseModel(AlternativesBaseModelPrivate &dd, QObject *parent)
0172     : QAbstractItemModel(parent), d_ptr(&dd)
0173 {
0174     d_ptr->q_ptr = this;
0175     d_ptr->load();
0176 }
0177 
0178 AlternativesBaseModel::~AlternativesBaseModel()
0179 {
0180     delete d_ptr;
0181 }
0182 
0183 bool AlternativesBaseModel::hasChildren(const QModelIndex &parent) const
0184 {
0185     if (!parent.isValid())
0186         return true;
0187 
0188     AltNode *n = static_cast<AltNode *>(parent.internalPointer());
0189     return n->childCount() > 0;
0190 }
0191 
0192 QModelIndex AlternativesBaseModel::index(int row, int column, const QModelIndex &parent) const
0193 {
0194     if (row < 0 || column < 0 || column >= columnCount(parent))
0195         return QModelIndex();
0196 
0197     AltNode *n = parent.isValid() ? static_cast<AltNode *>(parent.internalPointer()) : d_ptr->root();
0198     const QList<AltNode*> children = n->children();
0199     if (row < children.count())
0200         return createIndex(row, column, children.at(row));
0201 
0202     return QModelIndex();
0203 }
0204 
0205 QModelIndex AlternativesBaseModel::parent(const QModelIndex &index) const
0206 {
0207     if (!index.isValid())
0208         return QModelIndex();
0209 
0210     AltNode *n = static_cast<AltNode *>(index.internalPointer());
0211     return d_ptr->indexForItem(n->parent, index.column());
0212 
0213 }
0214 
0215 int AlternativesBaseModel::rowCount(const QModelIndex &parent) const
0216 {
0217     AltNode *n = parent.isValid() ? static_cast<AltNode *>(parent.internalPointer()) : d_ptr->root();
0218     return n->childCount();
0219 }
0220 
0221 
0222 class AlternativeItemsModelPrivate : public AlternativesBaseModelPrivate
0223 {
0224 public:
0225     AlternativeItemsModelPrivate(const QString &appName);
0226     ~AlternativeItemsModelPrivate();
0227 
0228     void load() Q_DECL_OVERRIDE;
0229     AltNode* root() const Q_DECL_OVERRIDE { return const_cast<AltRootNode *>(&m_root); }
0230 
0231     Q_DECLARE_PUBLIC(AlternativeItemsModel)
0232 
0233     void itemChanged(AltItemNode *node, ItemChanges changes);
0234     void loadItemNode(AltItemNode *node);
0235 
0236     bool isChanged(AltItemNode *node) const;
0237     AltAlternativeNode* findSelectedAlternative(AltItemNode *node, int *index) const;
0238 
0239     AltFilesManager *altManager;
0240     AltRootNode m_root;
0241     QString m_appName;
0242     KIconLoader *iconLoader;
0243     QIcon brokenAltIcon;
0244 };
0245 
0246 AlternativeItemsModelPrivate::AlternativeItemsModelPrivate(const QString &appName)
0247     : AlternativesBaseModelPrivate(), altManager(Q_NULLPTR)
0248     , m_appName(appName), iconLoader(new KIconLoader(m_appName))
0249     , brokenAltIcon(KDE::icon("alternative-broken", iconLoader))
0250 {
0251 #if defined(DISTRO_DPKG)
0252     altManager = new AltFilesManager("/var/lib/dpkg/alternatives");
0253 #elif defined(DISTRO_RPM_2)
0254     altManager = new AltFilesManager("/var/lib/alternatives");
0255 #elif defined(DISTRO_RPM)
0256     altManager = new AltFilesManager("/var/lib/rpm/alternatives");
0257 #else
0258     qCritical(KALT_LOG) << "Unsupported distribution for KAlternatives.";
0259 #endif
0260     if (altManager && !altManager->parsingOk())
0261     {
0262         qCDebug(KALT_LOG) << altManager->getErrorMsg();
0263         delete altManager;
0264         altManager = Q_NULLPTR;
0265     }
0266 }
0267 
0268 AlternativeItemsModelPrivate::~AlternativeItemsModelPrivate()
0269 {
0270     delete altManager;
0271 }
0272 
0273 void AlternativeItemsModelPrivate::load()
0274 {
0275     if (!altManager)
0276         return;
0277 
0278     ItemPtrList *itemslist = altManager->getGlobalAlternativeList();
0279     Q_FOREACH (Item *i, *itemslist)
0280     {
0281         AltItemNode *newItem = new AltItemNode(i, &m_root);
0282         m_root.m_children.append(newItem);
0283     }
0284 }
0285 
0286 void AlternativeItemsModelPrivate::itemChanged(AltItemNode *node, ItemChanges changes)
0287 {
0288     Q_Q(AlternativeItemsModel);
0289     if (changes & SelectionItemChange)
0290     {
0291         node->changed = true;
0292     }
0293     if (changes & AltNumItemChange)
0294     {
0295         node->nbrAltChanged = true;
0296     }
0297     if (changes & ModeItemChange)
0298     {
0299         node->modeChanged = true;
0300     }
0301     const QModelIndex index = indexForItem(node, 0);
0302     emit q->dataChanged(index, index);
0303 }
0304 
0305 void AlternativeItemsModelPrivate::loadItemNode(AltItemNode *node)
0306 {
0307     if (!node->m_children.isEmpty())
0308         return;
0309 
0310     AltsPtrList *alts = node->item->getAlternatives();
0311     Q_FOREACH (Alternative* a, *alts)
0312     {
0313         AltAlternativeNode *altnode = new AltAlternativeNode(a, node);
0314         node->m_children.append(altnode);
0315         altnode->selected = a->isSelected();
0316     }
0317 }
0318 
0319 bool AlternativeItemsModelPrivate::isChanged(AltItemNode *node) const
0320 {
0321     return node->changed || node->nbrAltChanged || node->modeChanged;
0322 }
0323 
0324 AltAlternativeNode* AlternativeItemsModelPrivate::findSelectedAlternative(AltItemNode *node, int *index) const
0325 {
0326     const int num = node->m_children.count();
0327     for (int i = 0; i < num; ++i)
0328     {
0329         AltAlternativeNode *altnode = node->m_children.at(i);
0330         if (altnode->selected)
0331         {
0332             *index = i;
0333             return altnode;
0334         }
0335     }
0336     *index = -1;
0337     return Q_NULLPTR;
0338 }
0339 
0340 
0341 AlternativeItemsModel::AlternativeItemsModel(const QString &appName, QObject *parent)
0342     : AlternativesBaseModel(*new AlternativeItemsModelPrivate(appName), parent)
0343 {
0344     Q_D(AlternativeItemsModel);
0345     d->iconLoader->setParent(this);
0346 }
0347 
0348 AlternativeItemsModel::~AlternativeItemsModel()
0349 {
0350 }
0351 
0352 int AlternativeItemsModel::columnCount(const QModelIndex &parent) const
0353 {
0354     Q_UNUSED(parent)
0355     return 1;
0356 }
0357 
0358 QVariant AlternativeItemsModel::data(const QModelIndex &index, int role) const
0359 {
0360     if (!index.isValid())
0361         return QVariant();
0362 
0363     Q_D(const AlternativeItemsModel);
0364     AltNode *n = static_cast<AltNode *>(index.internalPointer());
0365     if (AltItemNode *n_i = altnode_cast<AltItemNode>(n))
0366     {
0367         switch (role)
0368         {
0369             case Qt::DisplayRole:
0370                 return n_i->item->getName();
0371             case Qt::ToolTipRole:
0372             {
0373                 QString tip = n_i->item->getName();
0374                 if (n_i->item->isBroken())
0375                 {
0376                     tip += "\n\n";
0377                     tip += i18n("Broken alternative group.");
0378                 }
0379                 return tip;
0380             }
0381             case Qt::ForegroundRole:
0382                 if (d->isChanged(n_i))
0383                     return QColor(Qt::red);
0384                 break;
0385             case Qt::FontRole:
0386                 if (d->isChanged(n_i))
0387                 {
0388                     QFont f;
0389                     f.setBold(true);
0390                     return f;
0391                 }
0392                 break;
0393             case Qt::DecorationRole:
0394                 if (n_i->item->isBroken())
0395                     return d->brokenAltIcon;
0396                 break;
0397             case AltItemRole:
0398                 return QVariant::fromValue(n_i->item);
0399         }
0400     }
0401     return QVariant();
0402 }
0403 
0404 Qt::ItemFlags AlternativeItemsModel::flags(const QModelIndex &index) const
0405 {
0406     if (!index.isValid())
0407         return Qt::NoItemFlags;
0408 
0409     AltNode *n = static_cast<AltNode *>(index.internalPointer());
0410     if (AltItemNode *n_i = altnode_cast<AltItemNode>(n))
0411     {
0412         Qt::ItemFlags f = Qt::ItemIsSelectable;
0413         if (!n_i->item->isBroken())
0414             f |= Qt::ItemIsEnabled;
0415         return f;
0416     }
0417     return Qt::NoItemFlags;
0418 }
0419 
0420 bool AlternativeItemsModel::hasChildren(const QModelIndex &parent) const
0421 {
0422     return parent.isValid() ? false : AlternativesBaseModel::hasChildren(parent);
0423 }
0424 
0425 QVariant AlternativeItemsModel::headerData(int section, Qt::Orientation orientation, int role) const
0426 {
0427     if (orientation != Qt::Horizontal)
0428         return QVariant();
0429 
0430     switch (role)
0431     {
0432         case Qt::DisplayRole:
0433             if (section == 0)
0434                 return i18nc("Groups of alternatives", "Groups");
0435             break;
0436     }
0437     return QVariant();
0438 }
0439 
0440 QModelIndex AlternativeItemsModel::index(int row, int column, const QModelIndex &parent) const
0441 {
0442     return parent.isValid() ? QModelIndex() : AlternativesBaseModel::index(row, column, parent);
0443 }
0444 
0445 int AlternativeItemsModel::rowCount(const QModelIndex &parent) const
0446 {
0447     return parent.isValid() ? 0 : AlternativesBaseModel::rowCount(parent);
0448 }
0449 
0450 void AlternativeItemsModel::save()
0451 {
0452     Q_D(AlternativeItemsModel);
0453     if (!d->altManager)
0454         return;
0455 
0456     QModelIndexList changedIndexes;
0457     const int rows = d->m_root.m_children.count();
0458     for (int i = 0; i < rows; ++i)
0459     {
0460         AltItemNode *node = d->m_root.m_children.at(i);
0461         Item *item = node->item;
0462         bool itemChanged = false;
0463 
0464         if (node->changed)
0465         {
0466             int index = 0;
0467             AltAlternativeNode* altnode = d->findSelectedAlternative(node, &index);
0468             Q_ASSERT(altnode);
0469             QString selectError;
0470             if (!altnode->alternative->select(&selectError))
0471             {
0472                 qCDebug(KALT_LOG) << selectError << Qt::endl;
0473                 return;
0474             }
0475             node->changed = false;
0476             itemChanged = true;
0477         }
0478         if (node->nbrAltChanged || node->modeChanged)
0479         {
0480             QString parentPath = d->altManager->getAltDir() + '/' + item->getName();
0481 
0482             QFile origFile(parentPath);
0483             if (origFile.exists())
0484             {
0485                 origFile.remove();
0486             }
0487 
0488             if (origFile.open(QIODevice::WriteOnly))
0489             {
0490                 QTextStream stream(&origFile);
0491 
0492                 stream << Item::modeString(item->getMode()) << Qt::endl;
0493                 stream << item->getPath() << Qt::endl;
0494 
0495                 SlaveList *slaveList = item->getSlaves();
0496                 Q_FOREACH (Slave *slave, *slaveList)
0497                 {
0498                     stream << slave->slname << Qt::endl;
0499                     stream << slave->slpath << Qt::endl;
0500                 }
0501 
0502                 stream << Qt::endl;
0503 
0504                 AltsPtrList *altItemList = item->getAlternatives();
0505                 Q_FOREACH (Alternative *a, *altItemList)
0506                 {
0507                     stream << a->getPath() << Qt::endl;
0508                     stream << a->getPriority() << Qt::endl;
0509 
0510                     Q_FOREACH (const QString &slave, a->getSlaves())
0511                     {
0512                         stream << slave << Qt::endl;
0513                     }
0514                 }
0515 
0516                 stream << Qt::endl;
0517 
0518                 origFile.close();
0519             }
0520             node->nbrAltChanged = false;
0521             node->modeChanged = false;
0522             itemChanged = true;
0523         }
0524         if (itemChanged)
0525         {
0526             changedIndexes.append(createIndex(i, 0, node));
0527         }
0528     }
0529     Q_FOREACH (const QModelIndex &index, changedIndexes)
0530     {
0531         emit dataChanged(index, index);
0532     }
0533 }
0534 
0535 bool AlternativeItemsModel::isSupported() const
0536 {
0537     Q_D(const AlternativeItemsModel);
0538     return d->altManager;
0539 }
0540 
0541 
0542 class AlternativeAltModelPrivate : public AlternativesBaseModelPrivate
0543 {
0544 public:
0545     AlternativeAltModelPrivate(AlternativeItemsModel *itemModel, bool readOnly);
0546     ~AlternativeAltModelPrivate();
0547 
0548     void load() Q_DECL_OVERRIDE;
0549     AltNode* root() const Q_DECL_OVERRIDE { return m_root; }
0550 
0551     Q_DECLARE_PUBLIC(AlternativeAltModel)
0552 
0553     AltAlternativeNode* findHigherPriority(int *index) const;
0554     void searchDescription(Alternative *alternative) const;
0555 
0556     void statusChanged(int index);
0557 
0558     AlternativeItemsModelPrivate *parentModel;
0559     AltItemNode m_nullRoot;
0560     AltItemNode *m_root;
0561     bool m_readOnly;
0562 };
0563 
0564 AlternativeAltModelPrivate::AlternativeAltModelPrivate(AlternativeItemsModel *itemModel, bool readOnly)
0565     : AlternativesBaseModelPrivate()
0566     , parentModel(itemModel->d_func()), m_nullRoot(Q_NULLPTR, 0), m_root(&m_nullRoot), m_readOnly(readOnly)
0567 {
0568 }
0569 
0570 AlternativeAltModelPrivate::~AlternativeAltModelPrivate()
0571 {
0572 }
0573 
0574 void AlternativeAltModelPrivate::load()
0575 {
0576 }
0577 
0578 AltAlternativeNode* AlternativeAltModelPrivate::findHigherPriority(int *index) const
0579 {
0580     const int num = m_root->m_children.count();
0581     if (!num)
0582     {
0583         *index = 0;
0584         return Q_NULLPTR;
0585     }
0586 
0587     int id = 0;
0588     AltAlternativeNode* n = m_root->m_children.at(id);
0589     int priority = n->alternative->getPriority();
0590     for (int i = 1; i < num; ++i)
0591     {
0592         AltAlternativeNode* tmp = m_root->m_children.at(i);
0593         if (tmp->alternative->getPriority() > priority)
0594         {
0595             id = i;
0596             n = tmp;
0597             priority = n->alternative->getPriority();
0598         }
0599     }
0600     *index = id;
0601     return n;
0602 }
0603 
0604 static bool extractDescriptionFor(const QString &outputLine, const QString &name, QString *desc)
0605 {
0606     QString output = outputLine;
0607     int pos = output.indexOf('(');
0608     // look for the name of the search result, and discard it
0609     // in case it is not exactly what we requested
0610     if (pos == -1 || (output.left(pos -1) != name))
0611         return false;
0612 
0613     pos = output.indexOf(']');
0614     if (pos != -1)
0615     {
0616         output.remove(0, pos + 1);
0617     }
0618     pos = output.indexOf(')');
0619     if (pos != -1)
0620     {
0621         output.remove(0, pos + 1);
0622     }
0623     pos = output.indexOf('-');
0624     if (pos != -1)
0625     {
0626         output.remove(0, pos + 2);
0627     }
0628     *desc = output;
0629     return true;
0630 }
0631 
0632 void AlternativeAltModelPrivate::searchDescription(Alternative *alternative) const
0633 {
0634     QString exec = alternative->getPath();
0635     const int slashPos = exec.lastIndexOf('/');
0636     if (slashPos != -1)
0637         exec.remove(0, slashPos + 1);
0638 
0639     KProcess proc;
0640     proc.setProgram("whatis", QStringList() << exec);
0641     proc.setOutputChannelMode(KProcess::SeparateChannels);
0642     proc.setEnv("COLUMNS", QString::number(300));
0643     proc.start();
0644     proc.waitForStarted();
0645     proc.waitForFinished();
0646     if (proc.exitCode() == 0)
0647     {
0648         const QByteArray procOutput = proc.readAllStandardOutput();
0649         const QStringList outputLines = QString::fromLocal8Bit(procOutput.constData()).split('\n', Qt::SkipEmptyParts);
0650         Q_FOREACH (const QString &outLine, outputLines)
0651         {
0652             QString description;
0653             if (extractDescriptionFor(outLine, exec, &description))
0654             {
0655                 alternative->setDescription(description);
0656                 break;
0657             }
0658         }
0659     }
0660 }
0661 
0662 void AlternativeAltModelPrivate::statusChanged(int index)
0663 {
0664     if (m_root == &m_nullRoot)
0665         return;
0666 
0667     Q_Q(AlternativeAltModel);
0668     QComboBox *combo = q->sender() ? qobject_cast<QComboBox *>(q->sender()) : Q_NULLPTR;
0669     if (!combo)
0670         return;
0671 
0672     const Item::ItemMode mode = static_cast<Item::ItemMode>(combo->itemData(index).toInt());
0673     m_root->item->setMode(mode);
0674     ItemChanges changes = ModeItemChange;
0675     if (mode == Item::AutoMode)
0676     {
0677         int selectedIndex = 0;
0678         int higherPriorityIndex = 0;
0679         AltAlternativeNode *selectedaltnode = parentModel->findSelectedAlternative(m_root, &selectedIndex);
0680         AltAlternativeNode *newaltnode = findHigherPriority(&higherPriorityIndex);
0681         if (selectedIndex != higherPriorityIndex)
0682         {
0683             QModelIndexList indexes;
0684             selectedaltnode->selected = false;
0685             indexes.append(q->createIndex(selectedIndex, 0, selectedaltnode));
0686             newaltnode->selected = true;
0687             indexes.append(q->createIndex(higherPriorityIndex, 0, newaltnode));
0688             m_root->changed = true;
0689             Q_FOREACH (const QModelIndex &index, indexes)
0690             {
0691                 emit q->dataChanged(index, index);
0692             }
0693             changes |= SelectionItemChange;
0694         }
0695     }
0696     parentModel->itemChanged(m_root, changes);
0697 }
0698 
0699 
0700 AlternativeAltModel::AlternativeAltModel(AlternativeItemsModel *itemModel, bool readOnly, QObject *parent)
0701     : AlternativesBaseModel(*new AlternativeAltModelPrivate(itemModel, readOnly), parent)
0702 {
0703 }
0704 
0705 AlternativeAltModel::~AlternativeAltModel()
0706 {
0707 }
0708 
0709 int AlternativeAltModel::columnCount(const QModelIndex &parent) const
0710 {
0711     Q_UNUSED(parent)
0712     return 3;
0713 }
0714 
0715 QVariant AlternativeAltModel::data(const QModelIndex &index, int role) const
0716 {
0717     if (!index.isValid())
0718         return QVariant();
0719 
0720     Q_D(const AlternativeAltModel);
0721     AltNode *n = static_cast<AltNode *>(index.internalPointer());
0722     if (AltAlternativeNode *n_a = altnode_cast<AltAlternativeNode>(n))
0723     {
0724         switch (role)
0725         {
0726             case Qt::DisplayRole:
0727             {
0728                 switch (index.column())
0729                 {
0730                     case 0:
0731                         return n_a->alternative->getPath();
0732                     case 1:
0733                         return n_a->alternative->getPriority();
0734                     case 2:
0735                         if (n_a->alternative->getDescription().isEmpty())
0736                             d->searchDescription(n_a->alternative);
0737                         return Alternative::prettyDescription(n_a->alternative);
0738                 }
0739                 break;
0740             }
0741             case Qt::ToolTipRole:
0742             {
0743                 if (n_a->alternative->getDescription().isEmpty())
0744                     d->searchDescription(n_a->alternative);
0745                 KLocalizedString tip = n_a->alternative->isBroken()
0746                                      ? ki18nc("%1 is the alternative path, %2 its description",
0747                                               "%1\n(broken)\n\n%2")
0748                                      : ki18nc("%1 is the alternative path, %2 its description",
0749                                               "%1\n\n%2");
0750                 return tip.subs(n_a->alternative->getPath())
0751                           .subs(Alternative::prettyDescription(n_a->alternative))
0752                           .toString();
0753             }
0754             case Qt::EditRole:
0755                 if (index.column() == 0)
0756                     return n_a->selected;
0757                 break;
0758             case Qt::CheckStateRole:
0759                 if (index.column() == 0)
0760                     return n_a->selected ? Qt::Checked : Qt::Unchecked;
0761                 break;
0762             case Qt::DecorationRole:
0763                 if (index.column() == 0 && n_a->alternative->isBroken())
0764                     return d->parentModel->brokenAltIcon;
0765                 break;
0766             case AltAlternativeRole:
0767                 return QVariant::fromValue(n_a->alternative);
0768         }
0769     }
0770     return QVariant();
0771 }
0772 
0773 Qt::ItemFlags AlternativeAltModel::flags(const QModelIndex &index) const
0774 {
0775     if (!index.isValid())
0776         return Qt::NoItemFlags;
0777 
0778     Q_D(const AlternativeAltModel);
0779     AltNode *n = static_cast<AltNode *>(index.internalPointer());
0780     if (AltAlternativeNode *n_a = altnode_cast<AltAlternativeNode>(n))
0781     {
0782         Qt::ItemFlags f = Qt::ItemIsSelectable;
0783         if (!n_a->alternative->isBroken())
0784             f |= Qt::ItemIsEnabled;
0785         switch (index.column())
0786         {
0787             case 0:
0788                 return f | Qt::ItemIsUserCheckable;
0789             default:
0790                 return f;
0791         }
0792     }
0793     return Qt::NoItemFlags;
0794 }
0795 
0796 QVariant AlternativeAltModel::headerData(int section, Qt::Orientation orientation, int role) const
0797 {
0798     if (orientation != Qt::Horizontal)
0799         return QVariant();
0800 
0801     switch (role)
0802     {
0803         case Qt::DisplayRole:
0804             switch (section)
0805             {
0806                 case 0:
0807                     return i18n("Option");
0808                 case 1:
0809                     return i18n("Priority");
0810                 case 2:
0811                     return i18n("Description");
0812             }
0813             break;
0814     }
0815     return QVariant();
0816 }
0817 
0818 QModelIndex AlternativeAltModel::parent(const QModelIndex &index) const
0819 {
0820     Q_UNUSED(index);
0821     return QModelIndex();
0822 }
0823 
0824 bool AlternativeAltModel::setData(const QModelIndex &index, const QVariant &value, int role)
0825 {
0826     if (!index.isValid())
0827         return false;
0828 
0829     AltAlternativeNode *n = altnode_cast<AltAlternativeNode>(static_cast<AltNode *>(index.internalPointer()));
0830     if (!n)
0831         return false;
0832 
0833     Q_D(AlternativeAltModel);
0834     switch (role)
0835     {
0836         case Qt::CheckStateRole:
0837         {
0838             if (d->m_readOnly || n->alternative->isBroken())
0839                 break;
0840 
0841             const bool newValue = value.toBool();
0842             if (newValue)
0843             {
0844                 if (!n->selected)
0845                 {
0846                     QModelIndexList changedIndexes;
0847                     Q_FOREACH (AltAlternativeNode* node, d->m_root->m_children)
0848                     {
0849                         if (node->selected)
0850                         {
0851                             node->selected = false;
0852                             changedIndexes.append(d->indexForItem(node, index.column()));
0853                         }
0854                     }
0855                     n->selected = true;
0856                     changedIndexes.append(index);
0857                     Q_FOREACH (const QModelIndex &changedIndex, changedIndexes)
0858                     {
0859                         emit dataChanged(changedIndex, changedIndex);
0860                     }
0861                     ItemChanges changes = SelectionItemChange;
0862                     // when changing option, set the alternative to "manual" mode
0863                     if (d->m_root->item->getMode() == Item::AutoMode)
0864                     {
0865                         d->m_root->item->setMode(Item::ManualMode);
0866                         changes |= ModeItemChange;
0867                     }
0868                     d->parentModel->itemChanged(d->m_root, changes);
0869                     return true;
0870                 }
0871             }
0872             break;
0873         }
0874     }
0875     return false;
0876 }
0877 
0878 void AlternativeAltModel::setItem(Item *item)
0879 {
0880     Q_D(AlternativeAltModel);
0881     beginResetModel();
0882     d->m_root = &d->m_nullRoot;
0883     Q_FOREACH (AltItemNode *n, d->parentModel->m_root.m_children)
0884     {
0885         if (n->item == item)
0886         {
0887             d->m_root = n;
0888             break;
0889         }
0890     }
0891     if (d->m_root->item)
0892     {
0893         d->parentModel->loadItemNode(d->m_root);
0894     }
0895     endResetModel();
0896 }
0897 
0898 void AlternativeAltModel::addAlternative(Alternative *alt)
0899 {
0900     if (!alt)
0901         return;
0902 
0903     Q_D(AlternativeAltModel);
0904     if (alt->getParent() != d->m_root->item)
0905         return;
0906 
0907     const int childCount = d->m_root->m_children.count();
0908     beginInsertRows(QModelIndex(), childCount, childCount);
0909     d->m_root->item->addAlternative(alt);
0910     d->m_root->m_children.append(new AltAlternativeNode(alt, d->m_root));
0911     endInsertRows();
0912     d->parentModel->itemChanged(d->m_root, AltNumItemChange);
0913 }
0914 
0915 void AlternativeAltModel::removeAlternative(Alternative *alt)
0916 {
0917     if (!alt)
0918         return;
0919 
0920     Q_D(AlternativeAltModel);
0921     if (alt->getParent() != d->m_root->item)
0922         return;
0923 
0924     int altId = 0;
0925     const int childCount = d->m_root->m_children.count();
0926     for ( ; altId < childCount; ++altId)
0927     {
0928         if (d->m_root->m_children.at(altId)->alternative == alt)
0929             break;
0930     }
0931     if (altId == childCount)
0932         return;
0933 
0934     const bool wasSelected = d->m_root->m_children.at(altId)->selected;
0935     beginRemoveRows(QModelIndex(), altId, altId);
0936     d->m_root->item->delAlternativeByPath(alt->getPath());
0937     delete d->m_root->m_children.at(altId);
0938     d->m_root->m_children.removeAt(altId);
0939     endRemoveRows();
0940     if (wasSelected && !d->m_root->m_children.isEmpty())
0941     {
0942         int row = 0;
0943         AltAlternativeNode *node = d->findHigherPriority(&row);
0944         node->selected = true;
0945         const QModelIndex changedIndex = createIndex(row, 0, node);
0946         emit dataChanged(changedIndex, changedIndex);
0947     }
0948     d->parentModel->itemChanged(d->m_root, AltNumItemChange);
0949 }
0950 
0951 
0952 AlternativeItemProxyModel::AlternativeItemProxyModel(QObject *parent)
0953     : QSortFilterProxyModel(parent), m_showSingle(false)
0954 {
0955 }
0956 
0957 AlternativeItemProxyModel::~AlternativeItemProxyModel()
0958 {
0959 }
0960 
0961 void AlternativeItemProxyModel::setShowSingleAlternative(bool show)
0962 {
0963     if (show == m_showSingle)
0964         return;
0965 
0966     m_showSingle = show;
0967     invalidateFilter();
0968 }
0969 
0970 bool AlternativeItemProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0971 {
0972     Item *item = sourceModel()->index(source_row, 0, source_parent).data(AltItemRole).value<Item *>();
0973     return !item || m_showSingle || item->countAlternatives() > 1;
0974 }
0975 
0976 #include <moc_alternativemodels.cpp>