// This may look like C code, but it's really -*- C++ -*- /* * Copyright (C) 2008 Emweb bv, Herent, Belgium. * * See the LICENSE file for terms of use. */ #ifndef WBATCH_EDIT_PROXY_MODEL_H_ #define WBATCH_EDIT_PROXY_MODEL_H_ #include namespace Wt { /*! \class WBatchEditProxyModel Wt/WBatchEditProxyModel.h Wt/WBatchEditProxyModel.h * \brief A proxy model for %Wt's item models that provides batch editing. * * This proxy model presents data from a source model, and caches any * editing operation without affecting the underlying source model, * until commitAll() or revertAll() is called. In this way, you can * commit all the editing in batch to the underlying source model, * only when the user confirms the changes. * * All editing operations are supported: * - changing data (setData()) * - inserting and removing rows (insertRows() and removeRows()) * - inserting and removing columns (insertColumns() and removeColumns()) * * The model supports both simple tabular models, as well as * hierarchical (tree-like / treetable-like) models, with children * under items in the first column. * * Default values for a newly inserted row can be set using * setNewRowData() and flags for its items using setNewRowFlags(). * * \ingroup modelview */ class WT_API WBatchEditProxyModel : public WAbstractProxyModel { public: /*! \brief Constructor. */ WBatchEditProxyModel(); /*! \brief Destructor. */ virtual ~WBatchEditProxyModel(); /*! \brief Returns whether changes have not yet been committed. * * Returns whether have been made to the proxy model, which could be * committed using commitAll() or reverted using revertAll(). */ bool isDirty() const; /*! \brief Commits changes. * * This commits all changes to the source model. * * \sa revertAll() */ void commitAll(); /*! \brief Reverts changes. * * This reverts all changes. * * \sa commitAll() */ void revertAll(); /*! \brief Sets default data for a newly inserted row. * * You can use this method to initialize data for a newly inserted * row. */ void setNewRowData(int column, const cpp17::any& data, ItemDataRole role = ItemDataRole::Display); /*! \brief Sets the item flags for items in a newly inserted row. * * By default, flags() will return ItemFlag::Selectable. */ void setNewRowFlags(int column, WFlags flags); /*! \brief Configures data used to indicate a modified item. * * This sets \p data for item data role \p role to be returned by * data() for an item that is dirty (e.g. because it belongs to a * newly inserted row/column, or because new data has been set for * it. * * When \p role is Wt::ItemDataRole::StyleClass, the style class is appended * to any style already returned by the source model or set by * setNewRowData(). * * By default there is no dirty indication. */ void setDirtyIndication(ItemDataRole role, const cpp17::any& data); virtual WModelIndex mapFromSource(const WModelIndex& sourceIndex) const override; virtual WModelIndex mapToSource(const WModelIndex& proxyIndex) const override; /*! \brief Sets the source model. * * The source model provides the actual data for the proxy * model. * * Ownership of the source model is not transferred. * * All signals of the source model are propagated to the * proxy model. */ virtual void setSourceModel (const std::shared_ptr& sourceModel) override; virtual int columnCount(const WModelIndex& parent = WModelIndex()) const override; virtual int rowCount(const WModelIndex& parent = WModelIndex()) const override; virtual WModelIndex parent(const WModelIndex& index) const override; virtual WModelIndex index(int row, int column, const WModelIndex& parent = WModelIndex()) const override; using WAbstractItemModel::setData; using WAbstractItemModel::data; using WAbstractItemModel::setHeaderData; virtual cpp17::any data(const WModelIndex& index, ItemDataRole role = ItemDataRole::Display) const override; /*! \brief Sets item data. * * The default implementation will copy Wt::ItemDataRole::Edit data to * Wt::ItemDataRole::Display. You may want to specialize the model to provide * a more specialized editing behaviour. */ virtual bool setData(const WModelIndex& index, const cpp17::any& value, ItemDataRole role = ItemDataRole::Edit) override; virtual WFlags flags(const WModelIndex& index) const override; virtual cpp17::any headerData(int section, Orientation orientation = Orientation::Horizontal, ItemDataRole role = ItemDataRole::Display) const override; virtual bool insertRows(int row, int count, const WModelIndex& parent = WModelIndex()) override; virtual bool removeRows(int row, int count, const WModelIndex& parent = WModelIndex()) override; virtual bool insertColumns(int column, int count, const WModelIndex& parent = WModelIndex()) override; virtual bool removeColumns(int column, int count, const WModelIndex& parent = WModelIndex()) override; virtual void sort(int column, SortOrder order = SortOrder::Ascending) override; private: struct Cell { int row; int column; bool operator< (const Cell& other) const; Cell(int r, int c) : row(r), column(c) { } }; typedef std::map ValueMap; /* * For every proxy parent, we keep the following info: */ struct Item : public BaseItem { Item *insertedParent_; // !=0 when parent was inserted ValueMap editedValues_; std::vector removedRows_; // sorted, proxy rows std::vector insertedRows_; // sorted, proxy rows std::vector insertedItems_; // indexed by index in insertedRows_ std::vector removedColumns_; // sorted, proxy columns std::vector insertedColumns_; // sorted, proxy columns Item(const WModelIndex& sourceIndex); Item(Item *insertedParent); virtual ~Item(); }; bool submitting_; std::map newRowData_; std::map > newRowFlags_; ItemDataRole dirtyIndicationRole_; cpp17::any dirtyIndicationData_; std::vector modelConnections_; mutable ItemMap mappedIndexes_; void sourceColumnsAboutToBeInserted(const WModelIndex& parent, int start, int end); void sourceColumnsInserted(const WModelIndex& parent, int start, int end); void sourceColumnsAboutToBeRemoved(const WModelIndex& parent, int start, int end); void sourceColumnsRemoved(const WModelIndex& parent, int start, int end); void sourceRowsAboutToBeInserted(const WModelIndex& parent, int start, int end); void sourceRowsInserted(const WModelIndex& parent, int start, int end); void sourceRowsAboutToBeRemoved(const WModelIndex& parent, int start, int end); void sourceRowsRemoved(const WModelIndex& parent, int start, int end); void sourceDataChanged(const WModelIndex& topLeft, const WModelIndex& bottomRight); void sourceHeaderDataChanged(Orientation orientation, int start, int end); void sourceLayoutAboutToBeChanged(); void sourceLayoutChanged(); void sourceModelReset(); Item *itemFromSourceIndex(const WModelIndex& sourceIndex, bool autoCreate = true) const; Item *itemFromInsertedRow(Item *parentItem, const WModelIndex& index, bool autoCreate = true) const; Item *parentItemFromIndex(const WModelIndex& index) const; Item *itemFromIndex(const WModelIndex& index, bool autoCreate = true) const; bool isRemoved(const WModelIndex& sourceIndex) const; int adjustedProxyRow(Item *item, int sourceRow) const; int adjustedSourceRow(Item *item, int proxyRow) const; int adjustedProxyColumn(Item *item, int sourceColumn) const; int adjustedSourceColumn(Item *item, int proxyColumn) const; int adjustedProxyIndex(int sourceIndex, const std::vector& ins, const std::vector& rem) const; int adjustedSourceIndex(int proxyIndex, const std::vector& ins, const std::vector& rem) const; void insertIndexes(Item *item, std::vector& ins, std::vector *rowItems, int index, int count); void removeIndexes(Item *item, std::vector& ins, std::vector& rem, std::vector* rowItems, int index, int count); void deleteItemsUnder(Item *item, int row); void shift(std::vector& v, int row, int count); void shiftRows(ValueMap& v, int row, int count); void shiftRows(Item *item, int row, int count); void shiftColumns(ValueMap& v, int column, int count); void shiftColumns(Item *item, int column, int count); void resetMappings(); cpp17::any indicateDirty(ItemDataRole role, const cpp17::any& value) const; }; } #endif // WBATCH_EDIT_PROXY_MODEL_H_