diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d9e271498..cbe90b1d3 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool - scenetoolmode infocreator scriptedit dialoguesubview previewsubview + scenetoolmode infocreator scriptedit dialoguesubview previewsubview regionmap ) opencs_units (view/render diff --git a/apps/opencs/model/world/cell.hpp b/apps/opencs/model/world/cell.hpp index 89854312a..e6f3c8c35 100644 --- a/apps/opencs/model/world/cell.hpp +++ b/apps/opencs/model/world/cell.hpp @@ -9,6 +9,9 @@ namespace CSMWorld { /// \brief Wrapper for Cell record + /// + /// \attention The mData.mX and mData.mY fields of the ESM::Cell struct are not used. + /// Exterior cell coordinates are encoded in the cell ID. struct Cell : public ESM::Cell { std::string mId; diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 697c5f450..5f030bb52 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -1,6 +1,7 @@ #include "regionmap.hpp" +#include #include #include @@ -25,17 +26,28 @@ CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) mName = cell2.mName; } -CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const +CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { - return CellIndex (index.column()+mMin.first, - (mMax.second-mMin.second - index.row()-1)+mMin.second); + return mMin.move (index.column(), mMax.getY()-mMin.getY() - index.row()-1); } -QModelIndex CSMWorld::RegionMap::getIndex (const CellIndex& index) const +QModelIndex CSMWorld::RegionMap::getIndex (const CellCoordinates& index) const { // I hate you, Qt API naming scheme! - return QAbstractTableModel::index (mMax.second-mMin.second - (index.second-mMin.second)-1, - index.first-mMin.first); + return QAbstractTableModel::index (mMax.getY()-mMin.getY() - (index.getY()-mMin.getY())-1, + index.getX()-mMin.getX()); +} + +CSMWorld::CellCoordinates CSMWorld::RegionMap::getIndex (const Cell& cell) const +{ + std::istringstream stream (cell.mId); + + char ignore; + int x = 0; + int y = 0; + stream >> ignore >> x >> y; + + return CellCoordinates (x, y); } void CSMWorld::RegionMap::buildRegions() @@ -70,21 +82,21 @@ void CSMWorld::RegionMap::buildMap() { CellDescription description (cell); - CellIndex index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index = getIndex (cell2); mMap.insert (std::make_pair (index, description)); } } - std::pair mapSize = getSize(); + std::pair mapSize = getSize(); mMin = mapSize.first; mMax = mapSize.second; } -void CSMWorld::RegionMap::addCell (const CellIndex& index, const CellDescription& description) +void CSMWorld::RegionMap::addCell (const CellCoordinates& index, const CellDescription& description) { - std::map::iterator cell = mMap.find (index); + std::map::iterator cell = mMap.find (index); if (cell!=mMap.end()) { @@ -114,7 +126,7 @@ void CSMWorld::RegionMap::addCells (int start, int end) if (cell2.isExterior()) { - CellIndex index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index = getIndex (cell2); CellDescription description (cell); @@ -123,9 +135,9 @@ void CSMWorld::RegionMap::addCells (int start, int end) } } -void CSMWorld::RegionMap::removeCell (const CellIndex& index) +void CSMWorld::RegionMap::removeCell (const CellCoordinates& index) { - std::map::iterator cell = mMap.find (index); + std::map::iterator cell = mMap.find (index); if (cell!=mMap.end()) { @@ -160,7 +172,7 @@ void CSMWorld::RegionMap::updateRegions (const std::vector& regions std::for_each (regions2.begin(), regions2.end(), &Misc::StringUtils::lowerCase); std::sort (regions2.begin(), regions2.end()); - for (std::map::const_iterator iter (mMap.begin()); + for (std::map::const_iterator iter (mMap.begin()); iter!=mMap.end(); ++iter) { if (!iter->second.mRegion.empty() && @@ -176,90 +188,57 @@ void CSMWorld::RegionMap::updateRegions (const std::vector& regions void CSMWorld::RegionMap::updateSize() { - std::pair size = getSize(); + std::pair size = getSize(); + if (int diff = size.first.getX() - mMin.getX()) { - int diff = size.first.first - mMin.first; - - if (diff<0) - { - beginInsertColumns (QModelIndex(), 0, -diff-1); - mMin.first = size.first.first; - endInsertColumns(); - } - else if (diff>0) - { - beginRemoveColumns (QModelIndex(), 0, diff-1); - mMin.first = size.first.first; - endRemoveColumns(); - } + beginInsertColumns (QModelIndex(), 0, std::abs (diff)-1); + mMin = CellCoordinates (size.first.getX(), mMin.getY()); + endInsertColumns(); } + if (int diff = size.first.getY() - mMin.getY()) { - int diff = size.first.second - mMin.second; - - if (diff<0) - { - beginInsertRows (QModelIndex(), 0, -diff-1); - mMin.second = size.first.second; - endInsertRows(); - } - else if (diff>0) - { - beginRemoveRows (QModelIndex(), 0, diff-1); - mMin.second = size.first.second; - endRemoveRows(); - } + beginInsertRows (QModelIndex(), 0, std::abs (diff)-1); + mMin = CellCoordinates (mMin.getX(), size.first.getY()); + endInsertRows(); } + if (int diff = size.second.getX() - mMax.getX()) { - int diff = size.second.first - mMax.first; + int columns = columnCount(); if (diff>0) - { - int columns = columnCount(); beginInsertColumns (QModelIndex(), columns, columns+diff-1); - mMax.first = size.second.first; - endInsertColumns(); - } - else if (diff<0) - { - int columns = columnCount(); + else beginRemoveColumns (QModelIndex(), columns+diff, columns-1); - mMax.first = size.second.first; - endRemoveColumns(); - } + + mMax = CellCoordinates (size.second.getX(), mMax.getY()); + endInsertColumns(); } + if (int diff = size.second.getY() - mMax.getY()) { - int diff = size.second.second - mMax.second; + int rows = rowCount(); if (diff>0) - { - int rows = rowCount(); beginInsertRows (QModelIndex(), rows, rows+diff-1); - mMax.second = size.second.second; - endInsertRows(); - } - else if (diff<0) - { - int rows = rowCount(); + else beginRemoveRows (QModelIndex(), rows+diff, rows-1); - mMax.second = size.second.second; - endRemoveRows(); - } + + mMax = CellCoordinates (mMax.getX(), size.second.getY()); + endInsertRows(); } } -std::pair CSMWorld::RegionMap::getSize() - const +std::pair CSMWorld::RegionMap::getSize() const { const IdCollection& cells = mData.getCells(); int size = cells.getSize(); - CellIndex min (0, 0); - CellIndex max (0, 0); + CellCoordinates min (0, 0); + CellCoordinates max (0, 0); for (int i=0; i CSMWor if (cell2.isExterior()) { - CellIndex index (cell2.mData.mX, cell2.mData.mY); + CellCoordinates index = getIndex (cell2); if (min==max) { min = index; - max = std::make_pair (min.first+1, min.second+1); + max = min.move (1, 1); } else { - if (index.first=max.first) - max.first = index.first + 1; - - if (index.second=max.second) - max.second = index.second + 1; + if (index.getX()=max.getX()) + max = CellCoordinates (index.getX()+1, max.getY()); + + if (index.getY()=max.getY()) + max = CellCoordinates (max.getX(), index.getY() + 1); } } } @@ -323,7 +302,7 @@ int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const if (parent.isValid()) return 0; - return mMax.second-mMin.second; + return mMax.getY()-mMin.getY(); } int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const @@ -331,7 +310,7 @@ int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const if (parent.isValid()) return 0; - return mMax.first-mMin.first; + return mMax.getX()-mMin.getX(); } QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const @@ -343,7 +322,7 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const { /// \todo GUI class in non-GUI code. Needs to be addressed eventually. - std::map::const_iterator cell = + std::map::const_iterator cell = mMap.find (getIndex (index)); if (cell!=mMap.end()) @@ -370,13 +349,13 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const if (role==Qt::ToolTipRole) { - CellIndex cellIndex = getIndex (index); + CellCoordinates cellIndex = getIndex (index); std::ostringstream stream; - stream << cellIndex.first << ", " << cellIndex.second; + stream << cellIndex; - std::map::const_iterator cell = + std::map::const_iterator cell = mMap.find (cellIndex); if (cell!=mMap.end()) @@ -406,15 +385,33 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QString::fromUtf8 (stream.str().c_str()); } + if (role==Role_Region) + { + CellCoordinates cellIndex = getIndex (index); + + std::map::const_iterator cell = + mMap.find (cellIndex); + + if (cell!=mMap.end() && !cell->second.mRegion.empty()) + return QString::fromUtf8 (Misc::StringUtils::lowerCase (cell->second.mRegion).c_str()); + } + + if (role==Role_CellId) + { + CellCoordinates cellIndex = getIndex (index); + + std::ostringstream stream; + stream << "#" << cellIndex.getX() << " " << cellIndex.getY(); + + return QString::fromUtf8 (stream.str().c_str()); + } + return QVariant(); } Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const { - if (mMap.find (getIndex (index))!=mMap.end()) - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; - - return 0; + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) @@ -491,11 +488,7 @@ void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int const Cell& cell2 = cell.get(); if (cell2.isExterior()) - { - CellIndex index (cell2.mData.mX, cell2.mData.mY); - - removeCell (index); - } + removeCell (getIndex (cell2)); } } diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 7fb89f20a..7d7685e89 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -9,6 +9,7 @@ #include "record.hpp" #include "cell.hpp" +#include "cellcoordinates.hpp" namespace CSMWorld { @@ -23,7 +24,11 @@ namespace CSMWorld public: - typedef std::pair CellIndex; + enum Role + { + Role_Region = Qt::UserRole, + Role_CellId = Qt::UserRole+1 + }; private: @@ -39,27 +44,29 @@ namespace CSMWorld }; Data& mData; - std::map mMap; - CellIndex mMin; ///< inclusive - CellIndex mMax; ///< exclusive + std::map mMap; + CellCoordinates mMin; ///< inclusive + CellCoordinates mMax; ///< exclusive std::map mColours; ///< region ID, colour (RGBA) - CellIndex getIndex (const QModelIndex& index) const; + CellCoordinates getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) - QModelIndex getIndex (const CellIndex& index) const; + QModelIndex getIndex (const CellCoordinates& index) const; + + CellCoordinates getIndex (const Cell& cell) const; void buildRegions(); void buildMap(); - void addCell (const CellIndex& index, const CellDescription& description); + void addCell (const CellCoordinates& index, const CellDescription& description); ///< May be called on a cell that is already in the map (in which case an update is // performed) void addCells (int start, int end); - void removeCell (const CellIndex& index); + void removeCell (const CellCoordinates& index); ///< May be called on a cell that is not in the map (in which case the call is ignored) void addRegion (const std::string& region, unsigned int colour); @@ -78,7 +85,7 @@ namespace CSMWorld void updateSize(); - std::pair getSize() const; + std::pair getSize() const; public: @@ -89,6 +96,8 @@ namespace CSMWorld virtual int columnCount (const QModelIndex& parent = QModelIndex()) const; virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; + ///< \note Calling this function with role==Role_CellId may return the ID of a cell + /// that does not exist. virtual Qt::ItemFlags flags (const QModelIndex& index) const; diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index a33288025..0089143e9 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -15,23 +15,28 @@ CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) layout->setContentsMargins (0, 0, 0, 0); - RecordFilterBox *recordFilterBox = new RecordFilterBox (data, this); + mRecordFilterBox = new RecordFilterBox (data, this); - layout->addWidget (recordFilterBox); + layout->addWidget (mRecordFilterBox); setLayout (layout); - connect (recordFilterBox, + connect (mRecordFilterBox, SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (recordFilterChanged (boost::shared_ptr))); connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + mRecordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); - connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); + connect(this, SIGNAL(useFilterRequest(const std::string&)), mRecordFilterBox, SIGNAL(useFilterRequest(const std::string&))); setAcceptDrops(true); } +void CSVFilter::FilterBox::setRecordFilter (const std::string& filter) +{ + mRecordFilterBox->setFilter (filter); +} + void CSVFilter::FilterBox::dropEvent (QDropEvent* event) { std::vector data = dynamic_cast (event->mimeData())->getData(); diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 5954035fc..a8aa31953 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -16,10 +16,14 @@ namespace CSMWorld namespace CSVFilter { + class RecordFilterBox; + class FilterBox : public QWidget { Q_OBJECT + RecordFilterBox *mRecordFilterBox; + void dragEnterEvent (QDragEnterEvent* event); void dropEvent (QDropEvent* event); @@ -30,11 +34,13 @@ namespace CSVFilter FilterBox (CSMWorld::Data& data, QWidget *parent = 0); + void setRecordFilter (const std::string& filter); + signals: void recordFilterChanged (boost::shared_ptr filter); void recordDropped (std::vector& types, Qt::DropAction action); - void createFilterRequest(std::vector > >& filterSource, + void createFilterRequest(std::vector > >& filterSource, Qt::DropAction action); void useFilterRequest(const std::string& idOfFilter); }; diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index 2a1a1407f..f15b1e17a 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -15,18 +15,23 @@ CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *pare layout->addWidget (new QLabel ("Record Filter", this)); - EditWidget *editWidget = new EditWidget (data, this); + mEdit = new EditWidget (data, this); - layout->addWidget (editWidget); + layout->addWidget (mEdit); setLayout (layout); connect ( - editWidget, SIGNAL (filterChanged (boost::shared_ptr)), + mEdit, SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (filterChanged (boost::shared_ptr))); connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); + mEdit, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); - connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); + connect(this, SIGNAL(useFilterRequest(const std::string&)), mEdit, SLOT(useFilterRequest(const std::string&))); } + +void CSVFilter::RecordFilterBox::setFilter (const std::string& filter) +{ + mEdit->setText (QString::fromUtf8 (filter.c_str())); +} \ No newline at end of file diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index fa5c9c3c2..8e01310a3 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -17,14 +17,20 @@ namespace CSMWorld namespace CSVFilter { + class EditWidget; + class RecordFilterBox : public QWidget { Q_OBJECT + EditWidget *mEdit; + public: RecordFilterBox (CSMWorld::Data& data, QWidget *parent = 0); + void setFilter (const std::string& filter); + signals: void filterChanged (boost::shared_ptr filter); diff --git a/apps/opencs/view/world/regionmap.cpp b/apps/opencs/view/world/regionmap.cpp new file mode 100644 index 000000000..738de89ae --- /dev/null +++ b/apps/opencs/view/world/regionmap.cpp @@ -0,0 +1,346 @@ + +#include "regionmap.hpp" + +#include +#include +#include + +#include +#include +#include + +#include "../../model/doc/document.hpp" + +#include "../../model/world/regionmap.hpp" +#include "../../model/world/universalid.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/idtable.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" + +void CSVWorld::RegionMap::contextMenuEvent (QContextMenuEvent *event) +{ + QMenu menu (this); + + if (getUnselectedCells().size()>0) + menu.addAction (mSelectAllAction); + + if (selectionModel()->selectedIndexes().size()>0) + menu.addAction (mClearSelectionAction); + + if (getMissingRegionCells().size()>0) + menu.addAction (mSelectRegionsAction); + + int selectedNonExistentCells = getSelectedCells (false, true).size(); + + if (selectedNonExistentCells>0) + { + if (selectedNonExistentCells==1) + mCreateCellsAction->setText ("Create one Cell"); + else + { + std::ostringstream stream; + stream << "Create " << selectedNonExistentCells << " cells"; + mCreateCellsAction->setText (QString::fromUtf8 (stream.str().c_str())); + } + + menu.addAction (mCreateCellsAction); + } + + if (getSelectedCells().size()>0) + { + if (!mRegionId.empty()) + { + mSetRegionAction->setText (QString::fromUtf8 (("Set Region to " + mRegionId).c_str())); + menu.addAction (mSetRegionAction); + } + + menu.addAction (mUnsetRegionAction); + + menu.addAction (mViewInTableAction); + } + + if (selectionModel()->selectedIndexes().size()>0) + menu.addAction (mViewAction); + + menu.exec (event->globalPos()); +} + +QModelIndexList CSVWorld::RegionMap::getUnselectedCells() const +{ + const QAbstractItemModel *model = QTableView::model(); + + int rows = model->rowCount(); + int columns = model->columnCount(); + + QModelIndexList selected = selectionModel()->selectedIndexes(); + std::sort (selected.begin(), selected.end()); + + QModelIndexList all; + + for (int y=0; yindex (y, x); + if (model->data (index, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern)) + all.push_back (index); + } + + std::sort (all.begin(), all.end()); + + QModelIndexList list; + + std::set_difference (all.begin(), all.end(), selected.begin(), selected.end(), + std::back_inserter (list)); + + return list; +} + +QModelIndexList CSVWorld::RegionMap::getSelectedCells (bool existent, bool nonExistent) const +{ + const QAbstractItemModel *model = QTableView::model(); + + QModelIndexList selected = selectionModel()->selectedIndexes(); + + QModelIndexList list; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + bool exists = model->data (*iter, Qt::BackgroundRole)!=QBrush (Qt::DiagCrossPattern); + + if ((exists && existent) || (!exists && nonExistent)) + list.push_back (*iter); + } + + return list; +} + +QModelIndexList CSVWorld::RegionMap::getMissingRegionCells() const +{ + const QAbstractItemModel *model = QTableView::model(); + + QModelIndexList selected = selectionModel()->selectedIndexes(); + + std::set regions; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string region = + model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData(); + + if (!region.empty()) + regions.insert (region); + } + + QModelIndexList list; + + QModelIndexList unselected = getUnselectedCells(); + + for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) + { + std::string region = + model->data (*iter, CSMWorld::RegionMap::Role_Region).toString().toUtf8().constData(); + + if (!region.empty() && regions.find (region)!=regions.end()) + list.push_back (*iter); + } + + return list; +} + +void CSVWorld::RegionMap::setRegion (const std::string& regionId) +{ + QModelIndexList selected = getSelectedCells(); + + QAbstractItemModel *regionModel = model(); + + CSMWorld::IdTable *cellsModel = &dynamic_cast (* + mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + QString regionId2 = QString::fromUtf8 (regionId.c_str()); + + if (selected.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Set Region")); + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = regionModel->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + QModelIndex index = cellsModel->getModelIndex (cellId, + cellsModel->findColumnIndex (CSMWorld::Columns::ColumnId_Region)); + + mDocument.getUndoStack().push ( + new CSMWorld::ModifyCommand (*cellsModel, index, regionId2)); + } + + if (selected.size()>1) + mDocument.getUndoStack().endMacro(); +} + +CSVWorld::RegionMap::RegionMap (const CSMWorld::UniversalId& universalId, + CSMDoc::Document& document, QWidget *parent) +: QTableView (parent), mEditLock (false), mDocument (document) +{ + verticalHeader()->hide(); + horizontalHeader()->hide(); + + setSelectionMode (QAbstractItemView::ExtendedSelection); + + setModel (document.getData().getTableModel (universalId)); + + resizeColumnsToContents(); + resizeRowsToContents(); + + mSelectAllAction = new QAction (tr ("Select All"), this); + connect (mSelectAllAction, SIGNAL (triggered()), this, SLOT (selectAll())); + addAction (mSelectAllAction); + + mClearSelectionAction = new QAction (tr ("Clear Selection"), this); + connect (mClearSelectionAction, SIGNAL (triggered()), this, SLOT (clearSelection())); + addAction (mClearSelectionAction); + + mSelectRegionsAction = new QAction (tr ("Select Regions"), this); + connect (mSelectRegionsAction, SIGNAL (triggered()), this, SLOT (selectRegions())); + addAction (mSelectRegionsAction); + + mCreateCellsAction = new QAction (tr ("Create Cells Action"), this); + connect (mCreateCellsAction, SIGNAL (triggered()), this, SLOT (createCells())); + addAction (mCreateCellsAction); + + mSetRegionAction = new QAction (tr ("Set Region"), this); + connect (mSetRegionAction, SIGNAL (triggered()), this, SLOT (setRegion())); + addAction (mSetRegionAction); + + mUnsetRegionAction = new QAction (tr ("Unset Region"), this); + connect (mUnsetRegionAction, SIGNAL (triggered()), this, SLOT (unsetRegion())); + addAction (mUnsetRegionAction); + + mViewAction = new QAction (tr ("View Cells"), this); + connect (mViewAction, SIGNAL (triggered()), this, SLOT (view())); + addAction (mViewAction); + + mViewInTableAction = new QAction (tr ("View Cells in Table"), this); + connect (mViewInTableAction, SIGNAL (triggered()), this, SLOT (viewInTable())); + addAction (mViewInTableAction); +} + +void CSVWorld::RegionMap::setEditLock (bool locked) +{ + mEditLock = locked; +} + +void CSVWorld::RegionMap::selectAll() +{ + QModelIndexList unselected = getUnselectedCells(); + + for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) + selectionModel()->select (*iter, QItemSelectionModel::Select); +} + +void CSVWorld::RegionMap::clearSelection() +{ + selectionModel()->clearSelection(); +} + +void CSVWorld::RegionMap::selectRegions() +{ + QModelIndexList unselected = getMissingRegionCells(); + + for (QModelIndexList::const_iterator iter (unselected.begin()); iter!=unselected.end(); ++iter) + selectionModel()->select (*iter, QItemSelectionModel::Select); +} + +void CSVWorld::RegionMap::createCells() +{ + if (mEditLock) + return; + + QModelIndexList selected = getSelectedCells (false, true); + + CSMWorld::IdTable *cellsModel = &dynamic_cast (* + mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); + + if (selected.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Create cells")); + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + mDocument.getUndoStack().push (new CSMWorld::CreateCommand (*cellsModel, cellId)); + } + + if (selected.size()>1) + mDocument.getUndoStack().endMacro(); +} + +void CSVWorld::RegionMap::setRegion() +{ + if (mEditLock) + return; + + setRegion (mRegionId); +} + +void CSVWorld::RegionMap::unsetRegion() +{ + if (mEditLock) + return; + + setRegion (""); +} + +void CSVWorld::RegionMap::view() +{ + std::ostringstream hint; + hint << "c:"; + + QModelIndexList selected = selectionModel()->selectedIndexes(); + + bool first = true; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + if (first) + first = false; + else + hint << "; "; + + hint << cellId; + } + + emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Scene, "sys::default"), + hint.str()); +} + +void CSVWorld::RegionMap::viewInTable() +{ + std::ostringstream hint; + hint << "f:!or("; + + QModelIndexList selected = getSelectedCells(); + + bool first = true; + + for (QModelIndexList::const_iterator iter (selected.begin()); iter!=selected.end(); ++iter) + { + std::string cellId = model()->data (*iter, CSMWorld::RegionMap::Role_CellId). + toString().toUtf8().constData(); + + if (first) + first = false; + else + hint << ","; + + hint << "string(ID,\"" << cellId << "\")"; + } + + hint << ")"; + + emit editRequest (CSMWorld::UniversalId::Type_Cells, hint.str()); +} \ No newline at end of file diff --git a/apps/opencs/view/world/regionmap.hpp b/apps/opencs/view/world/regionmap.hpp new file mode 100644 index 000000000..c3757fe45 --- /dev/null +++ b/apps/opencs/view/world/regionmap.hpp @@ -0,0 +1,84 @@ +#ifndef CSV_WORLD_REGIONMAP_H +#define CSV_WORLD_REGIONMAP_H + +#include + +class QAction; + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + class UniversalId; +} + +namespace CSVWorld +{ + class RegionMap : public QTableView + { + Q_OBJECT + + QAction *mSelectAllAction; + QAction *mClearSelectionAction; + QAction *mSelectRegionsAction; + QAction *mCreateCellsAction; + QAction *mSetRegionAction; + QAction *mUnsetRegionAction; + QAction *mViewAction; + QAction *mViewInTableAction; + bool mEditLock; + CSMDoc::Document& mDocument; + std::string mRegionId; + + private: + + void contextMenuEvent (QContextMenuEvent *event); + + QModelIndexList getUnselectedCells() const; + ///< \note Non-existent cells are not listed. + + QModelIndexList getSelectedCells (bool existent = true, bool nonExistent = false) const; + ///< \param existant Include existant cells. + /// \param nonExistant Include non-existant cells. + + QModelIndexList getMissingRegionCells() const; + ///< Unselected cells within all regions that have at least one selected cell. + + void setRegion (const std::string& regionId); + ///< Set region Id of selected cells. + + public: + + RegionMap (const CSMWorld::UniversalId& universalId, CSMDoc::Document& document, + QWidget *parent = 0); + + void setEditLock (bool locked); + + signals: + + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); + + private slots: + + void selectAll(); + + void clearSelection(); + + void selectRegions(); + + void createCells(); + + void setRegion(); + + void unsetRegion(); + + void view(); + + void viewInTable(); + }; +} + +#endif diff --git a/apps/opencs/view/world/regionmapsubview.cpp b/apps/opencs/view/world/regionmapsubview.cpp index b82c1afb5..a7675a4a6 100644 --- a/apps/opencs/view/world/regionmapsubview.cpp +++ b/apps/opencs/view/world/regionmapsubview.cpp @@ -1,29 +1,27 @@ #include "regionmapsubview.hpp" -#include -#include +#include "regionmap.hpp" CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document) : CSVDoc::SubView (universalId) { - mTable = new QTableView (this); + mRegionMap = new RegionMap (universalId, document, this); - mTable->verticalHeader()->hide(); - mTable->horizontalHeader()->hide(); + setWidget (mRegionMap); - mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); - - mTable->setModel (document.getData().getTableModel (universalId)); - - mTable->resizeColumnsToContents(); - mTable->resizeRowsToContents(); - - setWidget (mTable); + connect (mRegionMap, SIGNAL (editRequest (const CSMWorld::UniversalId&, const std::string&)), + this, SLOT (editRequest (const CSMWorld::UniversalId&, const std::string&))); } void CSVWorld::RegionMapSubView::setEditLock (bool locked) { + mRegionMap->setEditLock (locked); +} +void CSVWorld::RegionMapSubView::editRequest (const CSMWorld::UniversalId& id, + const std::string& hint) +{ + focusId (id, hint); } \ No newline at end of file diff --git a/apps/opencs/view/world/regionmapsubview.hpp b/apps/opencs/view/world/regionmapsubview.hpp index 1655107af..524727901 100644 --- a/apps/opencs/view/world/regionmapsubview.hpp +++ b/apps/opencs/view/world/regionmapsubview.hpp @@ -3,7 +3,7 @@ #include "../doc/subview.hpp" -class QTableView; +class QAction; namespace CSMDoc { @@ -12,15 +12,23 @@ namespace CSMDoc namespace CSVWorld { + class RegionMap; + class RegionMapSubView : public CSVDoc::SubView { - QTableView *mTable; + Q_OBJECT + + RegionMap *mRegionMap; public: RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document); virtual void setEditLock (bool locked); + + private slots: + + void editRequest (const CSMWorld::UniversalId& id, const std::string& hint); }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index a2927c2f0..712b8f556 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -358,6 +358,9 @@ void CSVWorld::Table::cloneRecord() void CSVWorld::Table::moveUpRecord() { + if (mEditLock) + return; + QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) @@ -387,6 +390,9 @@ void CSVWorld::Table::moveUpRecord() void CSVWorld::Table::moveDownRecord() { + if (mEditLock) + return; + QModelIndexList selectedRows = selectionModel()->selectedRows(); if (selectedRows.size()==1) diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 2d08d186e..7f7b1477e 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -26,9 +26,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D layout->insertWidget (0, mTable = new Table (id, mBottom->canCreateAndDelete(), sorting, document), 2); - CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + mFilterBox = new CSVFilter::FilterBox (document.getData(), this); - layout->insertWidget (0, filterBox); + layout->insertWidget (0, mFilterBox); QWidget *widget = new QWidget; @@ -48,7 +48,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D mTable->selectionSizeUpdate(); mTable->viewport()->installEventFilter(this); mBottom->installEventFilter(this); - filterBox->installEventFilter(this); + mFilterBox->installEventFilter(this); if (mBottom->canCreateAndDelete()) { @@ -63,17 +63,17 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (mBottom, SIGNAL (requestFocus (const std::string&)), mTable, SLOT (requestFocus (const std::string&))); - connect (filterBox, + connect (mFilterBox, SIGNAL (recordFilterChanged (boost::shared_ptr)), mTable, SLOT (recordFilterChanged (boost::shared_ptr))); - connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), + connect(mFilterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); - connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); + connect(this, SIGNAL(useFilterRequest(const std::string&)), mFilterBox, SIGNAL(useFilterRequest(const std::string&))); connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), - filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + mFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); } void CSVWorld::TableSubView::setEditLock (bool locked) @@ -97,6 +97,15 @@ void CSVWorld::TableSubView::setStatusBar (bool show) mBottom->setStatusBar (show); } +void CSVWorld::TableSubView::useHint (const std::string& hint) +{ + if (hint.empty()) + return; + + if (hint[0]=='f' && hint.size()>=2) + mFilterBox->setRecordFilter (hint.substr (2)); +} + void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { emit cloneRequest(toClone.getId(), toClone.getType()); diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index 910fec325..3f82a7592 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -17,6 +17,11 @@ namespace CSMDoc class Document; } +namespace CSVFilter +{ + class FilterBox; +} + namespace CSVWorld { class Table; @@ -29,6 +34,7 @@ namespace CSVWorld Table *mTable; TableBottomBox *mBottom; + CSVFilter::FilterBox *mFilterBox; public: @@ -41,6 +47,8 @@ namespace CSVWorld virtual void setStatusBar (bool show); + virtual void useHint (const std::string& hint); + protected: bool eventFilter(QObject* object, QEvent *event);