# # # delete "res/overlays/added_missing.png" # # delete "res/overlays/dropped_added.png" # # delete "res/overlays/dropped_added_missing.png" # # delete "res/overlays/dropped_rename_target.png" # # delete "res/overlays/dropped_rename_target_missing.png" # # delete "res/overlays/dropped_unknown_ignored.png" # # delete "res/overlays/patched.png" # # delete "res/overlays/rename_source.png" # # delete "res/overlays/rename_source_added.png" # # delete "res/overlays/rename_source_added_missing.png" # # delete "res/overlays/rename_source_target.png" # # delete "res/overlays/rename_source_target_missing.png" # # delete "res/overlays/rename_source_unknown_ignored.png" # # delete "res/overlays/rename_target.png" # # delete "res/overlays/rename_target_missing.png" # # delete "res/overlays/unchanged.png" # # add_file "res/overlays/attributes_changed.png" # content [b378d7c64f993f0f067b2d44e3bed6d7d9e88a63] # # add_file "res/overlays/content_attributes_changed.png" # content [27e2d457a286431fce1a3edd2ab1d1595ad17b4e] # # add_file "res/overlays/content_changed.png" # content [458a3dd29e94f6855cad81403e6d86a33e3746da] # # add_file "res/overlays/invalid.png" # content [553b78c6a8a34cb8fd1a50602663903f86334673] # # add_file "res/overlays/renamed.png" # content [f6cc05b35d05b9859cc8a4ce953f6fa991ec45f9] # # patch "guitone.pro" # from [3fd3843b740f3d8859e4647497bbe315d23707ea] # to [1c7014b14dc27560b3de131703a35530cfdf98f3] # # patch "res/guitone.qrc" # from [f79689930cf514724cb11d18e452cba44630c2b6] # to [22e8f7b57aeec704d4237a2502392ad102f98a03] # # patch "res/overlays/added.png" # from [a3f896a257fd0fdeb51c38e8ccd5b569e445a3d2] # to [b67d5b01ff0bd6531e43bbfeb84cae1f89b58f81] # # patch "res/overlays/cdup.png" # from [9d51d79a1e09b20ef68e57325dbaece4cf7cd83f] # to [2e2c22895f69da156bfd0b3c0404472546251209] # # patch "res/overlays/dropped.png" # from [2d5080548f7ae87568f38c65e1afe4e4cef5f9ba] # to [1c141a16629b660b73db312730e864ba59f94826] # # patch "res/overlays/ignored.png" # from [875c76ae4acd950981e7af4a48b0bf994df48cda] # to [693320730927109d650f9101f1ee33ca5ab24503] # # patch "res/overlays/missing.png" # from [824de43651c95cd61882e2e584ec76b5f689b67d] # to [8de09f78d3970b31758b4df5bf84af0469ba892f] # # patch "res/overlays/unknown.png" # from [65f32f869d61bc9250db08354f1d4f9c0c4c8e2c] # to [6a570f0a61cef0ec50367ed47400651f812db5c8] # # patch "src/model/Inventory.cpp" # from [5f1c07927812405f93f4290686c37dd9b77a2098] # to [0b85908036de2c631f54520b95647496d4da7e94] # # patch "src/model/Inventory.h" # from [915db6a5693802dac41f20d550b54c86e89eb39a] # to [5d55d16c1fea9407de59a2c88b040f906f57bb3d] # # patch "src/model/InventoryItem.cpp" # from [439c5422abaf9736f91c1b462d16e200e0e1d721] # to [adff7c83b3515ff7672b1ebe4db8690c5733bacb] # # patch "src/model/InventoryItem.h" # from [0498e1d8a79804da34e4e789ad05e544026aa2b0] # to [f9a843860c2aa346bcd442e2d502dfac7a618386] # # patch "src/model/InventoryProxyModel.cpp" # from [e03c57d82a6e0ec6f1b842523a7a87278f7ac983] # to [8bb35e56494804f307d473bed5b01bae40b7bc37] # # patch "src/model/InventoryProxyModel.h" # from [61eb7da315ee67909835aeacd223141f8fc40fdd] # to [a7239616d0752740a3dfc9bd65d6d31ad2fe1973] # # patch "src/util/IconProvider.cpp" # from [eade6accc5f04c14c3f153b036d08e08e2c81499] # to [50010e2667d22c4df62b561115be814e694d6a27] # # patch "src/util/IconProvider.h" # from [0331678a530715a50344d5fb07943f02ad24bccf] # to [2b2f908c2f64b59def8971d445238252fde01e66] # # patch "src/view/InventoryView.cpp" # from [0207a6e08fed77d118b60410c1e432ee5ba458ad] # to [50873ebddf25b10ff14a51696a346c1c2e858170] # # patch "src/view/Splitter.cpp" # from [4ef814b5b7fcb03120f6f889aa279fdd0518df5f] # to [52ebbc624ae37b76a88cdb95b1e3f574a86e5b79] # # patch "src/view/WorkspaceMenuBar.cpp" # from [79395e00c0a347b59a71f00738882d0e58d69fce] # to [7f8026a44fa642d0bc6ef70be9694c4074e445f2] # # patch "src/view/WorkspaceMenuBar.h" # from [00dd3c433ab74935e3b0b8c4227a651f367724ae] # to [37f078fffd28f79bcdf6960b71884577cebea6f3] # # patch "src/view/WorkspaceWindow.cpp" # from [c0edca2787683a449e4f3a1a540cbeb2a122fe83] # to [da5b5622acc662e7f09e64ede5852682ca0d452f] # # set "res/overlays/attributes_changed.png" # attr "mtn:manual_merge" # value "true" # # set "res/overlays/content_attributes_changed.png" # attr "mtn:manual_merge" # value "true" # # set "res/overlays/content_changed.png" # attr "mtn:manual_merge" # value "true" # # set "res/overlays/invalid.png" # attr "mtn:manual_merge" # value "true" # # set "res/overlays/renamed.png" # attr "mtn:manual_merge" # value "true" # ============================================================ # res/overlays/attributes_changed.png is binary ============================================================ # res/overlays/content_attributes_changed.png is binary ============================================================ # res/overlays/content_changed.png is binary ============================================================ # res/overlays/invalid.png is binary ============================================================ # res/overlays/renamed.png is binary ============================================================ --- guitone.pro 3fd3843b740f3d8859e4647497bbe315d23707ea +++ guitone.pro 1c7014b14dc27560b3de131703a35530cfdf98f3 @@ -1,9 +1,9 @@ GUITONE_VERSION = "0.7" # # global version strings # GUITONE_VERSION = "0.7" -MIN_MTN_INT_VERSION = "5.0" -MAX_MTN_INT_VERSION = "6.0" +MIN_MTN_INT_VERSION = "6.0" +MAX_MTN_INT_VERSION = "7.0" # # common configuration ============================================================ --- res/guitone.qrc f79689930cf514724cb11d18e452cba44630c2b6 +++ res/guitone.qrc 22e8f7b57aeec704d4237a2502392ad102f98a03 @@ -9,26 +9,15 @@ icons/blue_dot.png icons/arrow_down.png overlays/added.png - overlays/added_missing.png - overlays/cdup.png overlays/dropped.png - overlays/dropped_added.png - overlays/dropped_added_missing.png - overlays/dropped_rename_target.png - overlays/dropped_rename_target_missing.png - overlays/dropped_unknown_ignored.png overlays/ignored.png overlays/missing.png - overlays/patched.png - overlays/rename_source.png - overlays/rename_source_added.png - overlays/rename_source_added_missing.png - overlays/rename_source_target.png - overlays/rename_source_target_missing.png - overlays/rename_source_unknown_ignored.png - overlays/rename_target.png - overlays/rename_target_missing.png - overlays/unchanged.png + overlays/renamed.png overlays/unknown.png + overlays/content_attributes_changed.png + overlays/content_changed.png + overlays/attributes_changed.png + overlays/invalid.png + overlays/cdup.png ============================================================ # res/overlays/added.png is binary ============================================================ # res/overlays/cdup.png is binary ============================================================ # res/overlays/dropped.png is binary ============================================================ # res/overlays/ignored.png is binary ============================================================ # res/overlays/missing.png is binary ============================================================ # res/overlays/unknown.png is binary ============================================================ --- src/model/Inventory.cpp 5f1c07927812405f93f4290686c37dd9b77a2098 +++ src/model/Inventory.cpp 0b85908036de2c631f54520b95647496d4da7e94 @@ -19,20 +19,15 @@ ***************************************************************************/ #include "Inventory.h" -#include "InventoryItem.h" #include "MonotoneUtil.h" -#include "IconProvider.h" +#include "BasicIOParser.h" #include Inventory::Inventory(QObject * parent) : QAbstractItemModel(parent), AutomateCommand(0), workspacePath() { - // create a dummy item since the view needs at least one item - // in the model, otherwise the app crashes - rootItem = new InventoryItem(); - regex = new QRegExp("^(R|D|[ ])(R|A|[ ])(M|P|U|I|[ ])\\s(\\d+)\\s(\\d+)\\s(.+)$"); - regex->setMinimal(true); + rootItem = new PseudoItem("__root", PseudoItem::Root); connect( this, SIGNAL(modelCreated()), @@ -43,7 +38,6 @@ Inventory::~Inventory() Inventory::~Inventory() { delete rootItem; - delete regex; } void Inventory::setWorkspacePath(const WorkspacePath & ws) @@ -51,10 +45,17 @@ void Inventory::setWorkspacePath(const W workspacePath = ws; } -void Inventory::readInventory() +void Inventory::readInventory(const QString & path) { I(!workspacePath.isEmpty()); - MonotoneTask task(QStringList() << "inventory"); + + QStringList cmd = QStringList() << "inventory"; + if (!path.isEmpty()) + { + cmd << path; + } + + MonotoneTask task(cmd); AutomateCommand::enqueueWorkspaceTask(workspacePath, task); } @@ -75,102 +76,52 @@ void Inventory::processTaskResult(const return; } - QStringList lines = task.getOutputUtf8().split("\n", QString::SkipEmptyParts); - QMap renameMap; - QMap::iterator renameIter; + BasicIOParser parser(task.getOutputUtf8()); + I(parser.parse()); + StanzaList stanzas = parser.getStanzas(); QList items; - InventoryItem * item; - int status(0); - int from_id(0); - int to_id(0); - QString path(""); - bool isDirectory(false); - - for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it) + foreach (Stanza st, stanzas) { - if (!parseInventoryLine(*it, status, from_id, to_id, path, isDirectory)) - { - continue; - } - - // this item is given a parent explicitely later on - item = new InventoryItem(isDirectory); - item->setPath(path); - item->setStatus(status); - - if (from_id > 0) - { - renameMap[-from_id] = item; - } - if (to_id > 0) - { - renameMap[to_id] = item; - } - + InventoryItem * item = new InventoryItem(st); items.push_back(item); } - int id = 0; - - while (true) - { - renameIter = renameMap.find(++id); - if (renameIter == renameMap.end()) break; - - renameMap[id]->setRenamedFrom(renameMap[-id]); - renameMap[-id]->setRenamedTo(renameMap[id]); - } - flatItemList.clear(); flatItemList = items; - // FIXME: we shouldn't really add a workspace root item here, but - // mtn automate inventory currently doesn't print the root workspace dir - InventoryItem * branch = new InventoryItem(true, true); - branch->setParent(rootItem); - branch->setPath("."); - branch->setStatus(0); - branch->setChildren(buildTreeRecursive(items, NULL)); - - // remove any older item rootItem->deleteAllChildren(); - rootItem->appendChild(branch); + rootItem->setChildren(buildTreeRecursive(items, NULL)); - // reset the model to repaint the view completly - // (all QModelIndexes are discarded through that, e.g. selections!) + // invalidate model indexes and request a repaint reset(); - // restore the normal cursor - qApp->restoreOverrideCursor(); - emit modelCreated(); } void Inventory::loadBranchName() { - QList children = rootItem->getChildren(); + QList children = rootItem->getChildren(); I(children.size() > 0); children[0]->setLabel(MonotoneUtil::getBranchName(workspacePath)); dataChanged(index(0, 0, QModelIndex()), index(0, 2, QModelIndex())); } -QList Inventory::buildTreeRecursive(QList & items, InventoryItem * parentItem) +QList Inventory::buildTreeRecursive(QList & items, ModelItem * parentItem) { - QList finalItems; + QList finalItems; QString parentPath = ""; - if (parentItem != NULL) + InventoryItem * parent = dynamic_cast(parentItem); + if (parent) { - parentPath = parentItem->getPath(); + parentPath = parent->getPath(); } - // add pseudo item "cd up" for each directory level - InventoryItem * cdUp = new InventoryItem(true); + ModelItem * cdUp = new PseudoItem("..", PseudoItem::CdUp); cdUp->setParent(parentItem); - cdUp->setPath(parentPath + QString("/..")); finalItems.append(cdUp); InventoryItem * currentItem; @@ -199,7 +150,7 @@ QList Inventory::buildT // it seems to be a valid item // - // if the item is directory a directory, catch items inside it + // if the item is a directory, catch items inside it if (currentItem->isDirectory()) { currentItem->setChildren(buildTreeRecursive(items, currentItem)); @@ -215,7 +166,7 @@ QModelIndex Inventory::index(int row, in QModelIndex Inventory::index(int row, int column, const QModelIndex & parent) const { - InventoryItem * parentItem; + ModelItem * parentItem; if (!parent.isValid()) { @@ -223,10 +174,11 @@ QModelIndex Inventory::index(int row, in } else { - parentItem = static_cast(parent.internalPointer()); + parentItem = static_cast(parent.internalPointer()); + I(parentItem); } - InventoryItem * childItem = parentItem->child(row); + ModelItem * childItem = parentItem->child(row); if (childItem) { @@ -236,71 +188,56 @@ QModelIndex Inventory::index(int row, in return QModelIndex(); } -int Inventory::columnCount(const QModelIndex &parent) const +int Inventory::columnCount(const QModelIndex & parent) const { - if (parent.isValid()) - { - return static_cast(parent.internalPointer())->columnCount(); - } - - return rootItem->columnCount(); + Q_UNUSED(parent); + return 3; } -QVariant Inventory::data(const QModelIndex &index, int role) const +QVariant Inventory::data(const QModelIndex & index, int role) const { if (!index.isValid()) { return QVariant(); } - InventoryItem * item = static_cast(index.internalPointer()); - - if ((role == Qt::DecorationRole) && (index.column() == 0)) - { - IconProvider * provider = IconProvider::singleton(); - return provider->getIcon(item); - } - else - { - return item->data(index.column(), role); - } + ModelItem * item = static_cast(index.internalPointer()); + I(item); + return item->data(index.column(), role); } -bool Inventory::setData(const QModelIndex & idx, const QVariant & value, int role) +Qt::ItemFlags Inventory::flags(const QModelIndex & index) const { - Q_UNUSED(idx); - Q_UNUSED(value); - Q_UNUSED(role); - return false; -} - -bool Inventory::setItemData(const QModelIndex & index, const QMap & roles) -{ - Q_UNUSED(index); - Q_UNUSED(roles); - return false; -} - -Qt::ItemFlags Inventory::flags(const QModelIndex &index) const -{ if (!index.isValid()) return 0; QFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; - InventoryItem * item = static_cast(index.internalPointer()); + ModelItem * item = static_cast(index.internalPointer()); + I(item); - if (item->isCdUp() || item->isRootDirectory()) return flags; + // no special flags for pseudo items + PseudoItem * psitem = dynamic_cast(item); + if (psitem) + { + return flags; + } - /* - // Disabled until we have figured out how to implement renaming properly - flags |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled; + InventoryItem * invitem = dynamic_cast(item); + if (invitem) + { + /* + // Disabled until we have figured out how to implement renaming properly + if (item->isNewNode()) + { + flags |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled; - if (item->isDirectory()) - { - flags |= Qt::ItemIsDropEnabled; + if (item->isDirectory()) + { + flags |= Qt::ItemIsDropEnabled; + } + } + */ } - */ - return flags; } @@ -322,121 +259,32 @@ QModelIndex Inventory::parent(const QMod return QModelIndex(); } - InventoryItem * childItem = static_cast(index.internalPointer()); - InventoryItem * parentItem = childItem->parent(); + ModelItem * childItem = static_cast(index.internalPointer()); + I(childItem); + ModelItem * parentItem = childItem->parent(); if (parentItem == rootItem) { return QModelIndex(); } + I(parentItem); return createIndex(parentItem->row(), 0, parentItem); } -int Inventory::rowCount(const QModelIndex& parent) const +int Inventory::rowCount(const QModelIndex & parent) const { - InventoryItem * parentItem = rootItem; + ModelItem * parentItem = rootItem; if (parent.isValid()) { - parentItem = static_cast(parent.internalPointer()); + parentItem = static_cast(parent.internalPointer()); } + I(parentItem); return parentItem->childCount(); } -bool Inventory::parseInventoryLine( - const QString &inputString, - int &status, - int &from_id, - int &to_id, - QString &path, - bool &isDirectory) -{ - if (regex->indexIn(inputString) == -1) - { - W(QString("Couldn't parse inventory line %1").arg(inputString)); - return false; - } - - QStringList list = regex->capturedTexts(); - status = 0; - - // the first match - if (list[1].compare("R") == 0) - { - status |= InventoryItem::RenamedFrom; - } else - if (list[1].compare("D") == 0) - { - status |= InventoryItem::Dropped; - } - else - if (list[1].compare(" ") != 0) - { - W(QString("Unknown status first tripel %1").arg(list[1])); - } - - // the second match - if (list[2].compare("R") == 0) - { - status |= InventoryItem::RenamedTo; - } else - if (list[2].compare("A") == 0) - { - status |= InventoryItem::Added; - } - else - if (list[2].compare(" ") != 0) - { - W(QString("Unknown status second tripel %1").arg(list[2])); - } - - // the third match - if (list[3].compare("M") == 0) - { - status |= InventoryItem::Missing; - } else - if (list[3].compare("P") == 0) - { - status |= InventoryItem::Patched; - } else - if (list[3].compare("U") == 0) - { - status |= InventoryItem::Unknown; - } else - if (list[3].compare("I") == 0) - { - status |= InventoryItem::Ignored; - } - else - if (list[3].compare(" ") == 0) - { - status |= InventoryItem::Unchanged; - } - else - { - W(QString("Unknown status third tripel %1").arg(list[3])); - } - - I(InventoryItem::ValidStates.contains(status)); - - // now determine if the file has been renamed - from_id = list[4].toInt(); - to_id = list[5].toInt(); - path = list[6].trimmed(); - - isDirectory = false; - if (path.endsWith('/')) - { - isDirectory = true; - path = path.left(path.length() - 1); - } - - // parsing was successful - return true; -} - //! TODO move this somewhere else without the inventory model dependency QMap Inventory::findUnaccountedRenames() { @@ -474,7 +322,7 @@ QMap Inventory // this is a new entry not recorded in the base roster if (missingItem->hasStatus(InventoryItem::Added) || - missingItem->hasStatus(InventoryItem::RenamedTo)) + missingItem->hasStatus(InventoryItem::RenameTarget)) { entry.path = missingItem->getPath(); entry.is_dir = missingItem->isDirectory(); ============================================================ --- src/model/Inventory.h 915db6a5693802dac41f20d550b54c86e89eb39a +++ src/model/Inventory.h 5d55d16c1fea9407de59a2c88b040f906f57bb3d @@ -22,12 +22,10 @@ #define INVENTORY_H #include "AutomateCommand.h" +#include "InventoryItem.h" -#include #include -class InventoryItem; - class Inventory : public QAbstractItemModel, public AutomateCommand { Q_OBJECT @@ -39,8 +37,6 @@ public: // needed Qt Model methods QVariant data(const QModelIndex &, int) const; - bool setData(const QModelIndex &, const QVariant &, int role = Qt::EditRole); - bool setItemData(const QModelIndex &, const QMap &); Qt::ItemFlags flags(const QModelIndex &) const; QVariant headerData(int, Qt::Orientation, int) const; QModelIndex index(int, int, const QModelIndex &) const; @@ -50,15 +46,13 @@ public slots: public slots: void setWorkspacePath(const WorkspacePath &); - void readInventory(); + void readInventory(const QString & path = QString()); private: void processTaskResult(const MonotoneTask &); - bool parseInventoryLine(const QString &, int &, int &, int &, QString &, bool &); - QList buildTreeRecursive(QList &, InventoryItem*); + QList buildTreeRecursive(QList &, ModelItem *); - InventoryItem * rootItem; - QRegExp * regex; + ModelItem * rootItem; QString branchName; QList flatItemList; WorkspacePath workspacePath; ============================================================ --- src/model/InventoryItem.cpp 439c5422abaf9736f91c1b462d16e200e0e1d721 +++ src/model/InventoryItem.cpp adff7c83b3515ff7672b1ebe4db8690c5733bacb @@ -19,158 +19,163 @@ ***************************************************************************/ #include "InventoryItem.h" -#include "vocab.h" #include #include -const int InventoryItem::RenamedFrom = 1; -const int InventoryItem::RenamedTo = 2; -const int InventoryItem::Added = 4; -const int InventoryItem::Dropped = 8; -const int InventoryItem::Missing = 16; -const int InventoryItem::Patched = 32; -const int InventoryItem::Unchanged = 64; -const int InventoryItem::Unknown = 128; -const int InventoryItem::Ignored = 256; +const int InventoryItem::RenameSource = 1; +const int InventoryItem::RenameTarget = 2; +const int InventoryItem::Added = 4; +const int InventoryItem::Dropped = 8; +const int InventoryItem::Missing = 16; +const int InventoryItem::Known = 32; +const int InventoryItem::Unknown = 64; +const int InventoryItem::Ignored = 128; +const int InventoryItem::Invalid = 256; +const int InventoryItem::ContentsChanged = 512; +const int InventoryItem::AttributesChanged = 1024; -// -// initialize the array with all 26 valid states (out of 45 possible) -// -const QList InventoryItem::ValidStates = QList() - << InventoryItem::Unchanged // ' ' - << InventoryItem::Patched // ' P' - << InventoryItem::Unknown // ' U' - << InventoryItem::Ignored // ' I' - << InventoryItem::Missing // ' M' - << ( InventoryItem::Added | InventoryItem::Patched ) // ' AP' - << ( InventoryItem::Added | InventoryItem::Missing ) // ' AM' - << ( InventoryItem::RenamedTo | InventoryItem::Unchanged ) // ' R ' - << ( InventoryItem::RenamedTo | InventoryItem::Patched ) // ' RP' - << ( InventoryItem::RenamedTo | InventoryItem::Missing ) // ' RM' - << ( InventoryItem::Dropped | InventoryItem::Unchanged ) // 'D ' - << ( InventoryItem::Dropped | InventoryItem::Unknown ) // 'D U' - << ( InventoryItem::Dropped | InventoryItem::Ignored ) // 'D I' - << ( InventoryItem::Dropped | InventoryItem::Added | InventoryItem::Patched ) // 'DAP' - << ( InventoryItem::Dropped | InventoryItem::Added | InventoryItem::Missing ) // 'DAM' - << ( InventoryItem::Dropped | InventoryItem::RenamedTo | InventoryItem::Unchanged ) // 'DR ' - << ( InventoryItem::Dropped | InventoryItem::RenamedTo | InventoryItem::Patched ) // 'DRP' - << ( InventoryItem::Dropped | InventoryItem::RenamedTo | InventoryItem::Missing ) // 'DRM' - << ( InventoryItem::RenamedFrom | InventoryItem::Unchanged ) // 'R ' - << ( InventoryItem::RenamedFrom | InventoryItem::Unknown ) // 'R U' - << ( InventoryItem::RenamedFrom | InventoryItem::Ignored ) // 'R I' - << ( InventoryItem::RenamedFrom | InventoryItem::Added | InventoryItem::Patched ) // 'RAP' - << ( InventoryItem::RenamedFrom | InventoryItem::Added | InventoryItem::Missing ) // 'RAM' - << ( InventoryItem::RenamedFrom | InventoryItem::RenamedTo | InventoryItem::Unchanged )// 'RR ' - << ( InventoryItem::RenamedFrom | InventoryItem::RenamedTo | InventoryItem::Patched ) // 'RRP' - << ( InventoryItem::RenamedFrom | InventoryItem::RenamedTo | InventoryItem::Missing ); // 'RRM' - -InventoryItem::InventoryItem(bool isDir /* =false */, bool isRoot /* =false */) +InventoryItem::InventoryItem(const Stanza & stanza) + : ModelItem(), status(0) { - parentItem = this; - path = ""; - label = ""; - status = 0; - dirFlag = isDir; - rootFlag = isRoot; -} + foreach (StanzaEntry en, stanza) + { + if (en.sym == "path") + { + I(en.vals.size() == 1); + path = en.vals.at(0); + continue; + } -InventoryItem::InventoryItem(const InventoryItem * other) -{ - dirFlag = other->isDirectory(); - rootFlag = other->isRootDirectory(); - renamed_from = other->getRenamedFrom(); - renamed_to = other->getRenamedTo(); - parentItem = other->parent(); - children = other->getChildren(); - status = other->getStatus(); - path = other->getPath(); - label = other->getLabel(); -} + if (en.sym == "old_path") + { + I(en.vals.size() == 1); + old_path = en.vals.at(0); + continue; + } -InventoryItem::~InventoryItem() -{ - qDeleteAll(children); - children.clear(); -} + if (en.sym == "new_path") + { + I(en.vals.size() == 1); + new_path = en.vals.at(0); + continue; + } -QString InventoryItem::getItemName() const -{ - if (label.size() > 0) - { - return label; - } - - return getFilename(); -} + if (en.sym == "old_type") + { + I(en.vals.size() == 1); + if (en.vals.at(0) == "file") + old_type = File; + else if (en.vals.at(0) == "directory") + old_type = Directory; + else + I(false); + continue; + } -void InventoryItem::deleteAllChildren(void) -{ - parentItem = this; - path = ""; - status = 0; - dirFlag = false; - rootFlag = false; - qDeleteAll(children); - children.clear(); -} + if (en.sym == "new_type") + { + I(en.vals.size() == 1); + if (en.vals.at(0) == "file") + new_type = File; + else if (en.vals.at(0) == "directory") + new_type = Directory; + else + I(false); + continue; + } -void InventoryItem::appendChild(InventoryItem* child) -{ - child->setParent(this); - children.append(child); -} + if (en.sym == "fs_type") + { + I(en.vals.size() == 1); + if (en.vals.at(0) == "file") + fs_type = File; + else if (en.vals.at(0) == "directory") + fs_type = Directory; + else if (en.vals.at(0) == "none") + fs_type = None; + else + I(false); + continue; + } -void InventoryItem::setChildren(QList items) -{ - // set the current element as parent for each children - QList::iterator iter; + if (en.sym == "status") + { + I(en.vals.size() > 0); + foreach (QString val, en.vals) + { + if (val == "rename_source") + status |= RenameSource; + else if (val == "rename_target") + status |= RenameTarget; + else if (val == "added") + status |= Added; + else if (val == "dropped") + status |= Dropped; + else if (val == "known") + status |= Known; + else if (val == "unknown") + status |= Unknown; + else if (val == "ignored") + status |= Ignored; + else if (val == "missing") + status |= Missing; + else if (val == "invalid") + status |= Invalid; + else + I(false); + } + continue; + } + if (en.sym == "changes") + { + I(en.vals.size() > 0 && en.vals.size() < 3); + foreach (QString val, en.vals) + { + if (val == "content") + status |= ContentsChanged; + else if (val == "attrs") + status |= AttributesChanged; + else + I(false); + } + continue; + } + } - InventoryItem *item; - for (iter = items.begin(); iter != items.end(); ++iter) - { - item = (*iter); - item->setParent(this); - } - - children.clear(); - children = items; + // we should have received at least a path, status and fs_type entry + I(!path.isNull()); + I(status > 0); + I(fs_type > 0); } -int InventoryItem::row() const +InventoryItem::InventoryItem(const InventoryItem * other) : ModelItem(other) { - if (parentItem) - return parentItem->children.indexOf(const_cast(this)); + path = other->getPath(); + old_path = other->getRenameSource(); + new_path = other->getRenameTarget(); - return 0; + fs_type = other->getFSType(); + old_type = other->getOldType(); + new_type = other->getNewType(); + + status = other->getStatus(); } QVariant InventoryItem::data(int column, int role) const { - if (role == Qt::DisplayRole) - { - // return column headers for root item - if (parentItem == this) - { - switch (column) - { - case 0: return QVariant(QString(tr("File"))); - case 1: return QVariant(QString(tr("Status"))); - case 2: return QVariant(QString(tr("Additional info"))); - default: return QVariant(); - } - } - + if (role == Qt::DisplayRole && parentItem != this) + { switch (column) - { - case 0: return QVariant(getItemName()); + { + case 0: return QVariant(getLabel()); case 1: return QVariant(getStatusString()); case 2: return QVariant(getRenameInfo()); default: return QVariant(); - } - } - else if (role == Qt::FontRole && column == 0) - { + } + } + else if (role == Qt::FontRole && column == 0) + { QFont font; font.setBold(false); if (hasChangedRecursive()) @@ -178,13 +183,25 @@ QVariant InventoryItem::data(int column, font.setBold(true); } return QVariant(font); - } - else - { - return QVariant(); - } + } + else if (role == Qt::ForegroundRole) + { + if (hasStatus(Invalid)) + { + return QVariant(Qt::red); + } + return QVariant(); + } + + return ModelItem::data(column, role); } +QString InventoryItem::getLabel() const +{ + if (!label.isEmpty()) return label; + return getFilename(); +} + QString InventoryItem::getFilename() const { int pos = path.lastIndexOf('/'); @@ -206,64 +223,95 @@ bool InventoryItem::hasStatus(int status bool InventoryItem::hasStatus(int statusBits) const { - return (status & statusBits) == statusBits; + return (status & statusBits) == statusBits; } bool InventoryItem::hasNotStatus(int statusBits) const { - return (status & statusBits) == 0; + return (status & statusBits) == 0; } int InventoryItem::getStatusRecursive() const { int overallStatus = status; - + if (!isDirectory()) { return overallStatus; } - + for (int i=0,s=children.size(); i(children.at(i)); + InventoryItem * item = static_cast(children.at(i)); overallStatus |= item->getStatusRecursive(); } - - + return overallStatus; } +bool InventoryItem::isNewNode() const +{ + return hasStatus(Known) || hasStatus(Missing) + || hasStatus(Added) || hasStatus(RenameTarget); +} + +bool InventoryItem::isOldNode() const +{ + return hasStatus(Dropped) || hasStatus(RenameSource); +} + +bool InventoryItem::isTracked() const +{ + return hasNotStatus(Ignored | Unknown); +} + +bool InventoryItem::hasChanged() const +{ + return hasStatus(Added) || hasStatus(Dropped) + || hasStatus(RenameSource) || hasStatus(RenameTarget) + || hasStatus(Invalid) || hasStatus(AttributesChanged) + || hasStatus(ContentsChanged); +} + bool InventoryItem::hasChangedRecursive() const { int state = getStatusRecursive(); - return + return (state & Added) == Added || (state & Dropped) == Dropped || - (state & RenamedFrom) == RenamedFrom || - (state & RenamedTo) == RenamedTo || - (state & Patched) == Patched; + (state & RenameSource) == RenameSource || + (state & RenameTarget) == RenameTarget || + (state & Invalid) == Invalid || + (state & AttributesChanged) == AttributesChanged || + (state & ContentsChanged) == ContentsChanged; } QString InventoryItem::getStatusString() const { - // do not return the status for - if (isCdUp()) return ""; - QStringList list; - - // at first the possible old states - if (this->hasStatus(InventoryItem::RenamedFrom)) + + // the patch states + if (this->hasStatus(InventoryItem::ContentsChanged)) { + list.append(tr("Contents modified")); + } + if (this->hasStatus(InventoryItem::AttributesChanged)) + { + list.append(tr("Attributes modified")); + } + + // the possible old node states + if (this->hasStatus(InventoryItem::RenameSource)) + { list.append(tr("Rename Source")); } - if (this->hasStatus(InventoryItem::Dropped)) { list.append(tr("Dropped")); } - - // now the possible new states - if (this->hasStatus(InventoryItem::RenamedTo)) + + // the possible new states + if (this->hasStatus(InventoryItem::RenameTarget)) { list.append(tr("Rename Target")); } @@ -271,20 +319,16 @@ QString InventoryItem::getStatusString() { list.append(tr("Added")); } - - // and finally the file states + + // the file states if (this->hasStatus(InventoryItem::Missing)) { list.append(tr("Missing")); } - if (this->hasStatus(InventoryItem::Patched)) + if (this->hasStatus(InventoryItem::Known)) { - list.append(tr("Modified")); + list.append(tr("Known")); } - if (this->hasStatus(InventoryItem::Unchanged)) - { - list.append(tr("Unchanged")); - } if (this->hasStatus(InventoryItem::Unknown)) { list.append(tr("Unknown")); @@ -293,25 +337,28 @@ QString InventoryItem::getStatusString() { list.append(tr("Ignored")); } - + + // a state which occurs if the node's file type has been + // mangled (f.e. node is recorded as file, but exists in fs as directory) + if (this->hasStatus(InventoryItem::Invalid)) + { + list.append(tr("Invalid")); + } + return list.join(", "); } QString InventoryItem::getRenameInfo() const { QStringList strings; - if (hasStatus(RenamedFrom)) + if (hasStatus(RenameSource)) { - InventoryItem * item = getRenamedTo(); - I(item); - strings << tr("new name: %1").arg(item->getPath()); + strings << tr("new name: %1").arg(new_path); } - - if (hasStatus(RenamedTo)) + + if (hasStatus(RenameTarget)) { - InventoryItem * item = getRenamedFrom(); - I(item); - strings << tr("old name: %1").arg(item->getPath()); + strings << tr("old name: %1").arg(old_path); } return strings.join(", "); } ============================================================ --- src/model/InventoryItem.h 0498e1d8a79804da34e4e789ad05e544026aa2b0 +++ src/model/InventoryItem.h f9a843860c2aa346bcd442e2d502dfac7a618386 @@ -21,84 +21,210 @@ #ifndef INVENTORY_ITEM_H #define INVENTORY_ITEM_H +#include "IconProvider.h" +#include "vocab.h" + #include -class InventoryItem : public QObject +class ModelItem : public QObject { Q_OBJECT public: - InventoryItem(bool isDir = false, bool isRoot = false); - InventoryItem(const InventoryItem *); - ~InventoryItem(void); + ModelItem(const QString & l = QString()) : parentItem(0), label(l) {} + ModelItem(const ModelItem * other) + { + parentItem = other->parent(); + children = other->getChildren(); + label = other->getLabel(); + } - inline void setRenamedFrom(InventoryItem * from) { renamed_from = from; } - inline void setRenamedTo(InventoryItem * to) { renamed_to = to; } - inline InventoryItem * getRenamedFrom(void) const { return renamed_from; } - inline InventoryItem * getRenamedTo(void) const { return renamed_to; } + virtual ~ModelItem() { deleteAllChildren(); } - inline void setParent(InventoryItem * p) { parentItem = p; } - inline InventoryItem * parent() const { return parentItem; } - inline QList getChildren(void) const { return children; } + virtual void setLabel(const QString & l) { label = l; } + virtual QString getLabel() const { return label; } - inline QString getPath(void) const { return path; } - inline void setPath(const QString & p) { path = p; } + void deleteAllChildren() + { + qDeleteAll(children); + children.clear(); + } - inline bool isCdUp() const { return getFilename() == ".."; } - inline bool isTracked() const { return hasNotStatus(Ignored | Unknown); } - inline bool isDirectory(void) const { return dirFlag; } - inline bool isRootDirectory(void) const { return rootFlag; } - inline int getStatus(void) const { return status; } - inline void setStatus(int st) { status = st; } - inline void setLabel(const QString & l) { label = l; } - inline QString getLabel() const { return label; } + void appendChild(ModelItem * child) + { + child->setParent(this); + children.append(child); + } - inline InventoryItem * child(int row) const { return children.value(row); } - inline int childCount(void) const { return children.count(); }; - inline int columnCount(void) const { return 3; } + void setChildren(QList items) + { + foreach (ModelItem * item, items) + { + item->setParent(this); + } + deleteAllChildren(); + children = items; + } - void setChildren(QList); - void deleteAllChildren(void); + QList getChildren() const + { + return children; + } - QString getRelativePath(const QString&) const; - QString getFilename(void) const; - QString getBaseDirectory(void) const; + void setParent(ModelItem * p) + { + parentItem = p; + } + ModelItem * parent() const + { + return parentItem; + } + + ModelItem * child(int row) const + { + if (row < children.size()) + { + return children.value(row); + } + return 0; + } + + int childCount() const + { + return children.count(); + } + + int row() const + { + if (parentItem) + { + return parentItem->children.indexOf(const_cast(this)); + } + return 0; + } + + virtual QVariant data(int column, int role) const + { + if (role == Qt::DisplayRole) + { + // return column headers for root item + if (parentItem == this) + { + switch (column) + { + case 0: return QVariant(QString(tr("File"))); + case 1: return QVariant(QString(tr("Status"))); + case 2: return QVariant(QString(tr("Additional info"))); + default: return QVariant(); + } + } + + switch (column) + { + case 0: return QVariant(label); + default: return QVariant(); + } + } + + if (role == Qt::DecorationRole && column == 0) + { + IconProvider * provider = IconProvider::singleton(); + return provider->getIcon(this); + } + + return QVariant(); + } + +protected: + ModelItem * parentItem; + QList children; + QString label; +}; + +class PseudoItem : public ModelItem +{ + Q_OBJECT +public: + enum Type { CdUp, Root }; + + PseudoItem(const QString & l, Type t) : ModelItem(l), type(t) + { + if (type == Root) + parentItem = this; + } + + bool isCdUp() const { return type == CdUp; } + bool isRoot() const { return type == Root; } + +private: + Type type; +}; + +class InventoryItem : public ModelItem +{ + Q_OBJECT +public: + enum FileType { File = 1, Directory, None }; + + InventoryItem(const Stanza &); + InventoryItem(const InventoryItem *); + + inline QString getRenameSource() const { return old_path; } + inline QString getRenameTarget() const { return new_path; } + inline FileType getFSType() const { return fs_type; } + inline FileType getOldType() const { return old_type; } + inline FileType getNewType() const { return new_type; } + inline QString getPath() const { return path; } + inline int getStatus() const { return status; } + inline bool isDirectory() const { return fs_type == Directory || + old_type == Directory || + new_type == Directory; } + + QString getRelativePath(const QString &) const; + QString getFilename() const; + QString getBaseDirectory() const; + QString getLabel() const; + QString getStatusString() const; + QString getRenameInfo() const; + bool hasStatus(int) const; bool hasNotStatus(int) const; + + bool isNewNode() const; + bool isOldNode() const; + bool isTracked() const; + bool hasChanged() const; + int getStatusRecursive() const; bool hasChangedRecursive() const; - /* needed for Qt model class */ - int row(void) const; - void appendChild(InventoryItem*); QVariant data(int, int) const; - static const int RenamedFrom; - static const int RenamedTo; + static const int RenameSource; + static const int RenameTarget; static const int Added; static const int Dropped; static const int Missing; - static const int Patched; - static const int Unchanged; + static const int Ignored; + static const int Known; static const int Unknown; - static const int Ignored; + static const int Invalid; + static const int AttributesChanged; + static const int ContentsChanged; - static const QList ValidStates; - private: - QString getItemName(void) const; - QString getStatusString(void) const; - QString getRenameInfo(void) const; + InventoryItem * parentItem; + QList children; - InventoryItem * renamed_from; - InventoryItem * renamed_to; - InventoryItem * parentItem; - QList children; + QString path; + QString old_path; + QString new_path; + + FileType fs_type; + FileType old_type; + FileType new_type; + int status; - bool dirFlag; - bool rootFlag; - QString path; - QString label; }; #endif ============================================================ --- src/model/InventoryProxyModel.cpp e03c57d82a6e0ec6f1b842523a7a87278f7ac983 +++ src/model/InventoryProxyModel.cpp 8bb35e56494804f307d473bed5b01bae40b7bc37 @@ -32,20 +32,29 @@ bool InventoryProxyModel::filterAcceptsR bool InventoryProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - InventoryItem * item = static_cast(index.internalPointer()); + ModelItem * item = static_cast(index.internalPointer()); - // display the pseudo CdUp item only in the file list regardless of the - // state of the item - if (item->isCdUp()) + PseudoItem * psitem = dynamic_cast(item); + if (psitem) { // only display this item if this is not the folder tree view // and the parent of the item's parent is invalid (i.e. there is no // way to cdUp anymore) - return !folderTree && sourceParent.parent().isValid(); + if (psitem->isCdUp()) + return !folderTree && sourceParent.parent().isValid(); + + // always return the root item + if (psitem->isRoot()) + return true; + + I(false); } + InventoryItem * invitem = dynamic_cast(item); + I(invitem); + // hide ignored files (not recursively) - if (item->hasStatus(InventoryItem::Ignored) && hideIgnored) + if (invitem->hasStatus(InventoryItem::Ignored) && hideIgnored) { return false; } @@ -53,66 +62,71 @@ bool InventoryProxyModel::filterAcceptsR bool acceptRow = true; // check the view options - int state = item->getStatusRecursive(); - bool patched = state & InventoryItem::Patched; - bool added = state & InventoryItem::Added; - bool dropped = state & InventoryItem::Dropped; - bool renamed = state & InventoryItem::RenamedFrom || - state & InventoryItem::RenamedTo; - bool unknown = state & InventoryItem::Unknown; - bool missing = state & InventoryItem::Missing; - bool ignored = state & InventoryItem::Ignored; - bool changed = patched || added || dropped || renamed; + int state = invitem->getStatusRecursive(); + bool cntchanged = state & InventoryItem::ContentsChanged; + bool attrchanged = state & InventoryItem::ContentsChanged; + bool added = state & InventoryItem::Added; + bool dropped = state & InventoryItem::Dropped; + bool renamed = state & InventoryItem::RenameSource || + state & InventoryItem::RenameTarget; + bool unknown = state & InventoryItem::Unknown; + bool missing = state & InventoryItem::Missing; + bool ignored = state & InventoryItem::Ignored; + bool invalid = state & InventoryItem::Invalid; + bool changed = cntchanged || attrchanged || added || dropped || renamed; - acceptRow &= viewOption != Changed || changed; - acceptRow &= viewOption != Patched || patched; - acceptRow &= viewOption != Added || added; - acceptRow &= viewOption != Dropped || dropped; - acceptRow &= viewOption != Renamed || renamed; - acceptRow &= viewOption != Unknown || unknown; - acceptRow &= viewOption != Missing || missing; - acceptRow &= viewOption != Ignored || ignored; + acceptRow &= viewOption != Changed || changed; + acceptRow &= viewOption != ContentsChanged || cntchanged; + acceptRow &= viewOption != AttributesChanged || attrchanged; + acceptRow &= viewOption != Added || added; + acceptRow &= viewOption != Dropped || dropped; + acceptRow &= viewOption != Renamed || renamed; + acceptRow &= viewOption != Unknown || unknown; + acceptRow &= viewOption != Missing || missing; + acceptRow &= viewOption != Ignored || ignored; + acceptRow &= viewOption != Invalid || invalid; // check if we should only display folders - acceptRow &= !folderTree || item->isDirectory(); + acceptRow &= !folderTree || invitem->isDirectory(); return acceptRow; } bool InventoryProxyModel::lessThan(const QModelIndex & left, const QModelIndex & right) const { - // TODO: Needs some tweaking for correct sorting when clicked on different headers - // f.e. status strings are sorted differently based on their translation - InventoryItem * itemLeft = static_cast(left.internalPointer()); - InventoryItem * itemRight = static_cast(right.internalPointer()); + ModelItem * itemLeft = static_cast(left.internalPointer()); + ModelItem * itemRight = static_cast(right.internalPointer()); + InventoryItem * invitemLeft = dynamic_cast(itemLeft); + InventoryItem * invitemRight = dynamic_cast(itemRight); + // We shall sort by the name of the item if (sortColumn == 0) { - // place cd up items always on the first position - if (itemLeft->isCdUp() || itemRight->isCdUp()) return false; + // place pseudo items always right on top + if (!invitemLeft || !invitemRight) return false; - if (itemLeft->isDirectory() && itemRight->isDirectory()) + if (invitemLeft->isDirectory() && invitemRight->isDirectory()) { - return itemLeft->getFilename() < itemRight->getFilename(); + return invitemLeft->getFilename() < invitemRight->getFilename(); } - if (!itemLeft->isDirectory() && !itemRight->isDirectory()) + if (!invitemLeft->isDirectory() && !invitemRight->isDirectory()) { - return itemLeft->getFilename() < itemRight->getFilename(); + return invitemLeft->getFilename() < invitemRight->getFilename(); } - if (itemLeft->isDirectory() && !itemRight->isDirectory()) + if (invitemLeft->isDirectory() && !invitemRight->isDirectory()) { return true; } - // last case: !itemLeft->isDirectory() && itemRight->isDirectory() + // last case: !invitemLeft->isDirectory() && invitemRight->isDirectory() return false; } // we shall sort only by status else if (sortColumn == 1) { - return itemLeft->getStatus() < itemRight->getStatus(); + return invitemLeft->getStatusString() < invitemRight->getStatusString(); } // anything else else ============================================================ --- src/model/InventoryProxyModel.h 61eb7da315ee67909835aeacd223141f8fc40fdd +++ src/model/InventoryProxyModel.h a7239616d0752740a3dfc9bd65d6d31ad2fe1973 @@ -27,8 +27,9 @@ public: { Q_OBJECT public: - enum ViewOption { All = 0, Changed, Patched, Added, Dropped, - Renamed, Missing, Unknown, Ignored }; + enum ViewOption { All = 0, Changed, ContentsChanged, AttributesChanged, + Added, Dropped, Renamed, Missing, Unknown, Ignored, + Invalid }; InventoryProxyModel(QObject *, bool); ~InventoryProxyModel(); ============================================================ --- src/util/IconProvider.cpp eade6accc5f04c14c3f153b036d08e08e2c81499 +++ src/util/IconProvider.cpp 50010e2667d22c4df62b561115be814e694d6a27 @@ -25,7 +25,6 @@ #include #include -const int IconProvider::CdUp = -1; IconProvider * IconProvider::instance = 0; IconProvider * IconProvider::singleton() @@ -37,125 +36,37 @@ IconProvider * IconProvider::singleton() return instance; } -/** - * There are 45 possible status combinations, of which 19 are invalid. - * We only provide for the 26 valid ones own icons - * (more info: http://venge.net/monotone/docs/Automation.html#Automation) - * - * Basic codes - * ----------- - * - * M 0 0 missing - * AP 0 0 added - * D 0 0 dropped - * R 1 0 renamed-from-this - * R 0 1 renamed-to-this - * P 0 0 patched - * 0 0 unchanged - * U 0 0 unknown - * I 0 0 ignored - * - * Combinations and used icons - * --------------------------- - * - * In general, we don't show the "patched" state if there is another - * state up to the item (added, renamed, etc.) - * - * ' ': unchanged.png - * ' P': patched.png - * ' U': unknown.png - * ' I': ignored.png - * ' M': missing.png - * - * ' AP': added.png - * ' AM': added_missing.png - * - * ' R ': rename_target.png - * ' RP': rename_target.png - * ' RM': rename_target_missing.png - * - * 'D ': dropped.png - * 'D U': dropped_unknown_ignored.png - * 'D I': dropped_unknown_ignored.png - * - * 'DAP': dropped_added.png - * 'DAM': dropped_added_missing.png - * - * 'DR ': dropped_rename_target.png - * 'DRP': dropped_rename_target.png - * 'DRM': dropped_rename_target_missing.png - * - * 'R ': rename_source.png - * 'R U': rename_source_unknown_ignored.png - * 'R I': rename_source_unknown_ignored.png - * - * 'RAP': rename_source_added.png - * 'RAM': rename_source_added_missing.png - * - * 'RR ': rename_source_target.png - * 'RRP': rename_source_target.png - * 'RRM': rename_source_target_missing.png - */ - IconProvider::IconProvider() { QStyle *style = QApplication::style(); - // // default icons if no state matches - // - fileIcons[0] = QIcon(style->standardPixmap(QStyle::SP_FileIcon)); - folderIcons[0] = QIcon(style->standardPixmap(QStyle::SP_DirClosedIcon)); - folderIcons[0].addPixmap(style->standardPixmap(QStyle::SP_DirOpenIcon), QIcon::Active, QIcon::On); - - // - // assign possible item states to the appropriate overlay - // - QMap states; - states[InventoryItem::Unchanged] = QString(":/overlays/unchanged.png"); - states[InventoryItem::Patched] = QString(":/overlays/patched.png"); - states[InventoryItem::Unknown] = QString(":/overlays/unknown.png"); - states[InventoryItem::Ignored] = QString(":/overlays/ignored.png"); - states[InventoryItem::Missing] = QString(":/overlays/missing.png"); - - states[InventoryItem::Added|InventoryItem::Patched] = QString(":/overlays/added.png"); - states[InventoryItem::Added|InventoryItem::Missing] = QString(":/overlays/added_missing.png"); - - states[InventoryItem::RenamedTo|InventoryItem::Unchanged] = QString(":/overlays/rename_target.png"); - states[InventoryItem::RenamedTo|InventoryItem::Patched] = QString(":/overlays/rename_target.png"); - states[InventoryItem::RenamedTo|InventoryItem::Missing] = QString(":/overlays/rename_target_missing.png"); - - states[InventoryItem::Dropped|InventoryItem::Unchanged] = QString(":/overlays/dropped.png"); - states[InventoryItem::Dropped|InventoryItem::Unknown] = QString(":/overlays/dropped_unknown_ignored.png"); - states[InventoryItem::Dropped|InventoryItem::Ignored] = QString(":/overlays/dropped_unknown_ignored.png"); - - states[InventoryItem::Dropped|InventoryItem::Added|InventoryItem::Patched] = QString(":/overlays/dropped_added.png"); - states[InventoryItem::Dropped|InventoryItem::Added|InventoryItem::Missing] = QString(":/overlays/dropped_added_missing.png"); + fileIcons["default"] = QIcon(style->standardPixmap(QStyle::SP_FileIcon)); + folderIcons["default"] = QIcon(style->standardPixmap(QStyle::SP_DirClosedIcon)); + folderIcons["default"].addPixmap(style->standardPixmap(QStyle::SP_DirOpenIcon), + QIcon::Active, QIcon::On); - states[InventoryItem::Dropped|InventoryItem::RenamedTo|InventoryItem::Unchanged] = QString(":/overlays/dropped_rename_target.png"); - states[InventoryItem::Dropped|InventoryItem::RenamedTo|InventoryItem::Patched] = QString(":/overlays/dropped_rename_target.png"); - states[InventoryItem::Dropped|InventoryItem::RenamedTo|InventoryItem::Missing] = QString(":/overlays/dropped_rename_target_missing.png"); + // node overlay states + QMap states; + states["added"] = QString(":/overlays/added.png"); + states["attr_changed"] = QString(":/overlays/attributes_changed.png"); + states["cont_attr_changed"] = QString(":/overlays/content_attributes_changed.png"); + states["cont_changed"] = QString(":/overlays/content_changed.png"); + states["dropped"] = QString(":/overlays/dropped.png"); + states["ignored"] = QString(":/overlays/ignored.png"); + states["invalid"] = QString(":/overlays/invalid.png"); + states["missing"] = QString(":/overlays/missing.png"); + states["renamed"] = QString(":/overlays/renamed.png"); + states["unknown"] = QString(":/overlays/unknown.png"); - states[InventoryItem::RenamedFrom|InventoryItem::Unchanged] = QString(":/overlays/rename_source.png"); - states[InventoryItem::RenamedFrom|InventoryItem::Unknown] = QString(":/overlays/rename_source_unknown_ignored.png"); - states[InventoryItem::RenamedFrom|InventoryItem::Ignored] = QString(":/overlays/rename_source_unknown_ignored.png"); + // special overlay states + states["cdup"] = QString(":/overlays/cdup.png"); - states[InventoryItem::RenamedFrom|InventoryItem::Added|InventoryItem::Patched] = QString(":/overlays/rename_source_added.png"); - states[InventoryItem::RenamedFrom|InventoryItem::Added|InventoryItem::Missing] = QString(":/overlays/rename_source_added_missing.png"); - - states[InventoryItem::RenamedFrom|InventoryItem::RenamedTo|InventoryItem::Unchanged] = QString(":/overlays/rename_source_target.png"); - states[InventoryItem::RenamedFrom|InventoryItem::RenamedTo|InventoryItem::Patched] = QString(":/overlays/rename_source_target.png"); - states[InventoryItem::RenamedFrom|InventoryItem::RenamedTo|InventoryItem::Missing] = QString(":/overlays/rename_source_target_missing.png"); - - // some special icon overlays - states[IconProvider::CdUp] = QString(":/overlays/cdup.png"); - - QMapIterator i(states); - + QMapIterator i(states); + while (i.hasNext()) { i.next(); - int state = i.key(); QImage overlay(i.value()); overlay.createAlphaMask(); @@ -164,20 +75,20 @@ IconProvider::IconProvider() QImage fileImage(style->standardPixmap(QStyle::SP_FileIcon).toImage()); QImage folderImageClosed(style->standardPixmap(QStyle::SP_DirClosedIcon).toImage()); QImage folderImageOpened(style->standardPixmap(QStyle::SP_DirOpenIcon).toImage()); - - // note: the overlays are scaled to fit the different icon sizes + + // note: the overlays are scaled to fit the different icon sizes // on different platforms / with different stylings, so the overlay // always perfectly fits the actual image - + // file icon QPainter filePainter(&fileImage); filePainter.drawImage( - 0, 0, + 0, 0, overlay.scaled(fileImage.width(), fileImage.height()) ); filePainter.end(); - fileIcons[state] = QIcon(QPixmap::fromImage(fileImage)); - + fileIcons[i.key()] = QIcon(QPixmap::fromImage(fileImage)); + // folder icons QPainter folderPainter1(&folderImageClosed); folderPainter1.drawImage( @@ -191,49 +102,120 @@ IconProvider::IconProvider() overlay.scaled(folderImageOpened.width(), folderImageOpened.height()) ); folderPainter2.end(); - - folderIcons[state] = QIcon(QPixmap::fromImage(folderImageClosed)); - folderIcons[state].addPixmap(QPixmap::fromImage(folderImageOpened), QIcon::Active, QIcon::On); + + folderIcons[i.key()] = QIcon(QPixmap::fromImage(folderImageClosed)); + folderIcons[i.key()].addPixmap(QPixmap::fromImage(folderImageOpened), + QIcon::Active, QIcon::On); } } IconProvider::~IconProvider() {} -QIcon IconProvider::getIcon(InventoryItem * item) const +QIcon IconProvider::getIcon(const ModelItem * item) const { - if (item->isCdUp()) + const PseudoItem * psitem = qobject_cast(item); + if (psitem) { - return folderIcons.value(IconProvider::CdUp); + if (psitem->isCdUp()) + { + return folderIcons.value("cdup"); + } + // FIXME: do we want to create a special node item? + return folderIcons.value("default"); } - - int status = item->getStatus(); - if (item->isDirectory()) - { - if (folderIcons.contains(status)) + const InventoryItem * invitem = qobject_cast(item); + if (invitem) + { + // we do not display all possible item state combinations, but + // give certain ones a precedence over others: + // + // 1) invalid state + // 2) missing state + // 3) content/attribute changes + // 4) content changes + // 5) attribute changes + // 6) tree changes (adds, drops, renames, new-state preferred!) + // 7) unknown node + // 8) ignored node + + QString state = "default"; + + if (invitem->hasStatus(InventoryItem::Invalid)) { - return folderIcons.value(status); + state = "invalid"; } - // default icon - return folderIcons.value(0); + else + if (invitem->hasStatus(InventoryItem::Missing)) + { + state = "missing"; + } + else + if (invitem->hasStatus(InventoryItem::AttributesChanged | + InventoryItem::ContentsChanged)) + { + state = "cont_attr_changed"; + } + else + if (invitem->hasStatus(InventoryItem::ContentsChanged)) + { + state = "cont_changed"; + } + else + if (invitem->hasStatus(InventoryItem::AttributesChanged)) + { + state = "attr_changed"; + } + else + if (invitem->hasStatus(InventoryItem::RenameTarget)) + { + state = "renamed"; + } + else + if (invitem->hasStatus(InventoryItem::Added)) + { + state = "added"; + } + else + if (invitem->hasStatus(InventoryItem::RenameSource)) + { + state = "renamed"; + } + else + if (invitem->hasStatus(InventoryItem::Dropped)) + { + state = "dropped"; + } + else + if (invitem->hasStatus(InventoryItem::Unknown)) + { + state = "unknown"; + } + else + if (invitem->hasStatus(InventoryItem::Ignored)) + { + state = "ignored"; + } + + if (invitem->isDirectory()) + { + return folderIcons.value(state); + } + + return fileIcons.value(state); } - - if (fileIcons.contains(status)) - { - return fileIcons.value(status); - } - // default icon - return fileIcons.value(0); + + // default file icon for any other ModelItem + return fileIcons.value("default"); } - QIcon IconProvider::getPlainFileIcon() const { - return fileIcons.value(0); + return fileIcons.value("default"); } QIcon IconProvider::getPlainFolderIcon() const { - return folderIcons.value(0); + return folderIcons.value("default"); } ============================================================ --- src/util/IconProvider.h 0331678a530715a50344d5fb07943f02ad24bccf +++ src/util/IconProvider.h 2b2f908c2f64b59def8971d445238252fde01e66 @@ -24,27 +24,27 @@ #include #include -class InventoryItem; +class ModelItem; class IconProvider { public: static IconProvider * singleton(); - - ~IconProvider(); - - QIcon getIcon(InventoryItem* item) const; + + ~IconProvider(); + + QIcon getIcon(const ModelItem * item) const; QIcon getPlainFileIcon() const; QIcon getPlainFolderIcon() const; - + static const int CdUp; private: IconProvider(); static IconProvider * instance; - - QMap fileIcons; - QMap folderIcons; + + QMap fileIcons; + QMap folderIcons; }; #endif ============================================================ --- src/view/InventoryView.cpp 0207a6e08fed77d118b60410c1e432ee5ba458ad +++ src/view/InventoryView.cpp 50873ebddf25b10ff14a51696a346c1c2e858170 @@ -241,55 +241,72 @@ void InventoryView::slotContextMenuReque if (indexList.size() == 1) { - QModelIndex sourceIndex = static_cast(indexList[0].model())->mapToSource(indexList[0]); + QModelIndex sourceIndex = static_cast + (indexList[0].model())->mapToSource(indexList[0]); + ModelItem * item = static_cast + (sourceIndex.internalPointer()); + InventoryItem * invitem = dynamic_cast + (item); - InventoryItem* item = static_cast(sourceIndex.internalPointer()); + // only display the popup for real workspace items + if (!invitem) + return; - if (item->isDirectory()) + if (invitem->isDirectory()) { menu.addAction(actChdir); } - menu.addAction(actOpen); + if (invitem->isNewNode() && + invitem->hasNotStatus(InventoryItem::Missing) && + invitem->hasNotStatus(InventoryItem::Invalid)) + { + menu.addAction(actOpen); + } - if (item->hasStatus(InventoryItem::Unknown)) + if (invitem->hasStatus(InventoryItem::Unknown)) { menu.addAction(actAdd); menu.addAction(actIgnore); } - if (item->hasStatus(InventoryItem::Ignored)) + if (invitem->hasStatus(InventoryItem::Ignored)) { menu.addAction(actUnignore); } - if (item->hasChangedRecursive()) + if (invitem->hasChangedRecursive()) { menu.addAction(actCommit); menu.addAction(actRevisionDiff); } - if (item->hasNotStatus(InventoryItem::Ignored) && - item->hasNotStatus(InventoryItem::Unknown) && - !item->isRootDirectory() && !item->isCdUp()) + // FIXME: we currently exclude the workspace root for this. + // While its reasonable for the remove action, the root could + // actually be renamed by pivot_root - decide whether we want + // to implement that here or offer a workspace menu and dialog + // for this rather rare use case + if (invitem->hasNotStatus(InventoryItem::Ignored) && + invitem->hasNotStatus(InventoryItem::Unknown) && + !invitem->getPath().isEmpty()) { menu.addAction(actRemove); menu.addAction(actRename); // as long as it is not unchanged, it can be converted - if (item->hasNotStatus(InventoryItem::Unchanged)) + if (invitem->hasChanged()) { menu.addAction(actRevert); - if (item->hasNotStatus(InventoryItem::Added) && - !item->isDirectory()) + if (invitem->hasStatus(InventoryItem::ContentsChanged) && + invitem->hasNotStatus(InventoryItem::Added)) { menu.addAction(actFileDiff); } } - if (item->hasNotStatus(InventoryItem::Added) && - !item->isDirectory()) + if (invitem->hasNotStatus(InventoryItem::Added) && + !invitem->isDirectory()) { menu.addAction(actFileHistory); } @@ -300,34 +317,38 @@ void InventoryView::slotContextMenuReque // QFont activeFont; activeFont.setBold(true); - QFont normalFont; - normalFont.setBold(false); - if (item->isDirectory()) + if (invitem->isDirectory()) { - actOpen->setFont(normalFont); actChdir->setFont(activeFont); } else { - if (item->hasStatus(InventoryItem::Patched) && - item->hasNotStatus(InventoryItem::Added)) + if (invitem->hasStatus(InventoryItem::ContentsChanged) && + invitem->hasNotStatus(InventoryItem::Added)) { actFileDiff->setFont(activeFont); - actOpen->setFont(normalFont); } else + if (invitem->isNewNode() && + invitem->hasNotStatus(InventoryItem::Missing) && + invitem->hasNotStatus(InventoryItem::Invalid)) { - actFileDiff->setFont(normalFont); actOpen->setFont(activeFont); } + else + if (invitem->hasChanged()) + { + actRevert->setFont(activeFont); + } } menu.exec(pos); } else { - InventoryItem * item; + ModelItem * item; + InventoryItem * invitem; QModelIndex sourceIndex; QStringList actions; @@ -344,24 +365,29 @@ void InventoryView::slotContextMenuReque QMap actionCounter; - foreach(QString action, actions) + foreach (QString action, actions) { actionCounter.insert(action, 0); } - foreach(QModelIndex index, indexList) + foreach (QModelIndex index, indexList) { sourceIndex = static_cast(index.model()) ->mapToSource(index); - item = static_cast(sourceIndex.internalPointer()); + item = static_cast(sourceIndex.internalPointer()); + invitem = dynamic_cast(item); - if (!item->isTracked()) + // skip actions on anything other than InventoryItems + if (!invitem) + continue; + + if (!invitem->isTracked()) { actionCounter["add"]++; - if (item->hasStatus(InventoryItem::Unknown)) + if (invitem->hasStatus(InventoryItem::Unknown)) { actionCounter["ignore"]++; } @@ -372,7 +398,7 @@ void InventoryView::slotContextMenuReque continue; } - if (item->hasChangedRecursive()) + if (invitem->hasChangedRecursive()) { actionCounter["commit"]++; actionCounter["revert"]++; @@ -420,16 +446,20 @@ void InventoryView::changeDirectory(cons void InventoryView::changeDirectory(const QModelIndex & proxyIndex) { - QModelIndex source = static_cast(model())->mapToSource(proxyIndex); - InventoryItem * item = static_cast(source.internalPointer()); + QModelIndex source = dynamic_cast + (model())->mapToSource(proxyIndex); if (type == FileList) { // retranslate the proxy index to an index of the current model QModelIndex proxyFileIndex = - static_cast(model())->mapFromSource(source); + dynamic_cast(model())->mapFromSource(source); - if (item->isCdUp()) + ModelItem * item = static_cast + (source.internalPointer()); + + PseudoItem * psitem = dynamic_cast(item); + if (psitem && psitem->isCdUp()) { // list the contents of the parent of the parent directory setRootIndex(proxyFileIndex.parent().parent()); @@ -458,9 +488,13 @@ void InventoryView::slotOpen() QModelIndex index(getSingleSelection()); if (!index.isValid()) return; - InventoryItem * item = static_cast(index.internalPointer()); + ModelItem * item = static_cast(index.internalPointer()); + InventoryItem * invitem = dynamic_cast(item); - emit openFile(item->getPath()); + if (!invitem) + return; + + emit openFile(invitem->getPath()); } void InventoryView::slotAdd() @@ -509,15 +543,19 @@ void InventoryView::slotFileDiff() QModelIndex index(getSingleSelection()); if (!index.isValid()) return; - InventoryItem * item = static_cast(index.internalPointer()); + ModelItem * item = static_cast(index.internalPointer()); + InventoryItem * invitem = dynamic_cast(item); - if (item->isDirectory() || !item->hasStatus(InventoryItem::Patched)) + if (!invitem) + return; + + if (!invitem->hasStatus(InventoryItem::ContentsChanged)) { - D("File is a directory or not patched/unknown."); + D("File has no content changes"); return; } - emit diffFile(item->getPath()); + emit diffFile(invitem->getPath()); } void InventoryView::slotFileHistory() @@ -525,22 +563,22 @@ void InventoryView::slotFileHistory() QModelIndex index(getSingleSelection()); if (!index.isValid()) return; - InventoryItem * item = static_cast(index.internalPointer()); + ModelItem * item = static_cast(index.internalPointer()); + InventoryItem * invitem = dynamic_cast(item); - QString path = item->getPath(); + if (!invitem) + return; + QString path = invitem->getPath(); + // determine the original path, if needed - if (item->hasStatus(InventoryItem::RenamedFrom)) + if (invitem->hasStatus(InventoryItem::RenameSource)) { - InventoryItem * newItem = item->getRenamedTo(); - I(newItem != 0); - path = newItem->getPath(); + path = invitem->getRenameTarget(); } - if (item->hasStatus(InventoryItem::RenamedTo)) + if (invitem->hasStatus(InventoryItem::RenameTarget)) { - InventoryItem * oldItem = item->getRenamedFrom(); - I(oldItem != 0); - path = oldItem->getPath(); + path = invitem->getRenameSource(); } emit fileHistory(path); @@ -551,9 +589,13 @@ void InventoryView::slotRevisionDiff() QModelIndex index(getSingleSelection()); if (!index.isValid()) return; - InventoryItem * item = static_cast(index.internalPointer()); + ModelItem * item = static_cast(index.internalPointer()); + InventoryItem * invitem = dynamic_cast(item); - emit diffRevision(item->getPath(), QString(), QString()); + if (!invitem) + return; + + emit diffRevision(invitem->getPath(), QString(), QString()); } QModelIndex InventoryView::getSingleSelection(bool mapToSource /* = true */) @@ -583,17 +625,23 @@ void InventoryView::itemClicked(const QM QModelIndex sourceIndex = static_cast(index.model())->mapToSource(index); - InventoryItem * item = static_cast(sourceIndex.internalPointer()); + ModelItem * item = static_cast(index.internalPointer()); + InventoryItem * invitem = dynamic_cast(item); + PseudoItem * psitem = dynamic_cast(item); - if (item->isDirectory()) + if ((invitem && invitem->isDirectory()) || + (psitem && psitem->isCdUp())) { emit directoryChanged(index); changeDirectory(index); return; } - if (item->hasStatus(InventoryItem::Patched) && - item->hasNotStatus(InventoryItem::Added)) + if (!invitem) + return; + + if (invitem->hasStatus(InventoryItem::ContentsChanged) && + invitem->hasNotStatus(InventoryItem::Added)) { slotFileDiff(); return; ============================================================ --- src/view/Splitter.cpp 4ef814b5b7fcb03120f6f889aa279fdd0518df5f +++ src/view/Splitter.cpp 52ebbc624ae37b76a88cdb95b1e3f574a86e5b79 @@ -20,6 +20,7 @@ #include "Splitter.h" #include "Settings.h" +#include "vocab.h" #include @@ -32,6 +33,7 @@ Splitter::~Splitter() Splitter::~Splitter() { + L(QString("saving state of splitter %1").arg(objectName())); saveState(); } ============================================================ --- src/view/WorkspaceMenuBar.cpp 79395e00c0a347b59a71f00738882d0e58d69fce +++ src/view/WorkspaceMenuBar.cpp 7f8026a44fa642d0bc6ef70be9694c4074e445f2 @@ -33,9 +33,12 @@ WorkspaceMenuBar::WorkspaceMenuBar(QWidg actionAll_changed_files = new QAction(tr("All changed files"), this); actionAll_changed_files->setCheckable(true); actionAll_changed_files->setShortcut(tr("Alt+C")); - actionPatched_files = new QAction(tr("Patched files"), this); - actionPatched_files->setCheckable(true); - actionPatched_files->setShortcut(tr("Alt+P")); + actionChangedContent_files = new QAction(tr("Files with changed contents"), this); + actionChangedContent_files->setCheckable(true); + actionChangedContent_files->setShortcut(tr("Alt+P")); + actionChangedAttributes_files = new QAction(tr("Files with changed attributes"), this); + actionChangedAttributes_files->setCheckable(true); + actionChangedAttributes_files->setShortcut(tr("Alt+T")); actionAdded_files = new QAction(tr("Added files"), this); actionAdded_files->setCheckable(true); actionAdded_files->setShortcut(tr("Alt+N")); @@ -54,18 +57,23 @@ WorkspaceMenuBar::WorkspaceMenuBar(QWidg actionIgnored_files = new QAction(tr("Ignored files"), this); actionIgnored_files->setCheckable(true); actionIgnored_files->setShortcut(tr("Alt+I")); + actionInvalid_files = new QAction(tr("Invalid files"), this); + actionInvalid_files->setCheckable(true); + actionInvalid_files->setShortcut(tr("Alt+V")); // FIXME: yeah, I know, magic values suck, but registering enums // to be usable in Qt's signal/slot system sucks for the moment, too actionAll_files->setData(QVariant(0)); actionAll_changed_files->setData(QVariant(1)); - actionPatched_files->setData(QVariant(2)); - actionAdded_files->setData(QVariant(3)); - actionRemoved_files->setData(QVariant(4)); - actionRenamed_files->setData(QVariant(5)); - actionMissing_files->setData(QVariant(6)); - actionUnknown_files->setData(QVariant(7)); - actionIgnored_files->setData(QVariant(8)); + actionChangedContent_files->setData(QVariant(2)); + actionChangedAttributes_files->setData(QVariant(3)); + actionAdded_files->setData(QVariant(4)); + actionRemoved_files->setData(QVariant(5)); + actionRenamed_files->setData(QVariant(6)); + actionMissing_files->setData(QVariant(7)); + actionUnknown_files->setData(QVariant(8)); + actionIgnored_files->setData(QVariant(9)); + actionInvalid_files->setData(QVariant(10)); menuShow = new QMenu(tr("Show"), this); @@ -73,12 +81,15 @@ WorkspaceMenuBar::WorkspaceMenuBar(QWidg menuShow->addSeparator(); menuShow->addAction(actionAll_changed_files); menuShow->addAction(actionAdded_files); - menuShow->addAction(actionPatched_files); + menuShow->addAction(actionChangedContent_files); + menuShow->addAction(actionChangedAttributes_files); menuShow->addAction(actionRemoved_files); menuShow->addAction(actionRenamed_files); menuShow->addAction(actionMissing_files); menuShow->addAction(actionUnknown_files); menuShow->addAction(actionIgnored_files); + menuShow->addSeparator(); + menuShow->addAction(actionInvalid_files); actionHide_ignored_files = new QAction(tr("Hide ignored files"), this); actionHide_ignored_files->setShortcut(tr("Ctrl+H")); ============================================================ --- src/view/WorkspaceMenuBar.h 00dd3c433ab74935e3b0b8c4227a651f367724ae +++ src/view/WorkspaceMenuBar.h 37f078fffd28f79bcdf6960b71884577cebea6f3 @@ -45,13 +45,15 @@ protected: QAction * actionExpand_tree; QAction * actionAll_files; QAction * actionAll_changed_files; - QAction * actionPatched_files; + QAction * actionChangedContent_files; + QAction * actionChangedAttributes_files; QAction * actionAdded_files; QAction * actionRemoved_files; QAction * actionRenamed_files; QAction * actionMissing_files; QAction * actionUnknown_files; QAction * actionIgnored_files; + QAction * actionInvalid_files; QAction * actionUpdate_workspace; QAction * actionCommit_revision; ============================================================ --- src/view/WorkspaceWindow.cpp c0edca2787683a449e4f3a1a540cbeb2a122fe83 +++ src/view/WorkspaceWindow.cpp da5b5622acc662e7f09e64ede5852682ca0d452f @@ -301,24 +301,28 @@ void WorkspaceWindow::readAttributes(con void WorkspaceWindow::readAttributes(const QModelIndex & index) { - QModelIndex sourceIndex = static_cast(index.model())->mapToSource(index); - InventoryItem * item = static_cast(sourceIndex.internalPointer()); + QModelIndex sourceIndex = static_cast + (index.model())->mapToSource(index); + ModelItem * item = static_cast + (sourceIndex.internalPointer()); + InventoryItem * invitem = dynamic_cast + (item); - if (item->isRootDirectory() || item->isCdUp()) + if (!invitem) { - D("item is pseudo item (root or cdup)"); + D("item is not inventory item"); attrModel->revert(); return; } - if (item->hasStatus(InventoryItem::Dropped) || !item->isTracked()) + if (invitem->isOldNode() || !invitem->isTracked()) { - D("item is not tracked or dropped"); + D("item is old node or not tracked"); attrModel->revert(); return; } - attrModel->readAttributes(item->getPath()); + attrModel->readAttributes(invitem->getPath()); } void WorkspaceWindow::invalidWorkspaceFormat(const QString & error)