/**************************************************************************** ** ** This file is part of the LibreCAD project, a 2D CAD program ** ** Copyright (C) 2015 A. Stebich (librecad@mail.lordofbikes.de) ** Copyright (C) 2011 Rallaz (rallazz@gmail.com) ** Copyright (C) 2010-2011 R. van Twisk (librecad@rvt.dds.nl) ** ** ** This file is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ** ** This copyright notice MUST APPEAR in all copies of the script! ** **********************************************************************/ #include "qg_layerwidget.h" #include "qg_actionhandler.h" #include "qc_applicationwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include "rs_debug.h" QG_LayerModel::QG_LayerModel(QObject * parent) : QAbstractTableModel(parent) { layerVisible = QIcon(":/icons/visible.svg"); layerHidden = QIcon(":/icons/invisible.svg"); layerDefreeze = QIcon(":/icons/unlocked.svg"); layerFreeze = QIcon(":/icons/locked.svg"); layerPrint = QIcon(":/icons/print.svg"); layerNoPrint = QIcon(":/icons/noprint.svg"); layerConstruction = QIcon(":/icons/construction_layer.svg"); layerNoConstruction = QIcon(":/icons/noconstruction.svg"); } int QG_LayerModel::rowCount ( const QModelIndex & /*parent*/ ) const { return listLayer.size(); } QModelIndex QG_LayerModel::parent ( const QModelIndex & /*index*/ ) const { return QModelIndex(); } QModelIndex QG_LayerModel::index ( int row, int column, const QModelIndex & /*parent*/ ) const { if ( row >= listLayer.size() || row < 0) return QModelIndex(); return createIndex ( row, column); } void QG_LayerModel::setLayerList(RS_LayerList* ll) { /* since 4.6 the recommended way is to use begin/endResetModel() * TNick */ beginResetModel(); listLayer.clear(); if (ll == NULL) { endResetModel(); return; } for (unsigned i=0; i < ll->count(); ++i) { listLayer.append(ll->at(i)); } setActiveLayer(ll->getActive()); std::sort( listLayer.begin(), listLayer.end(), [](const RS_Layer *s1, const RS_Layer *s2)-> bool{ return s1->getName() < s2->getName(); } ); //called to force redraw endResetModel(); } RS_Layer *QG_LayerModel::getLayer( int row ) { if ( row >= listLayer.size() || row < 0) return NULL; return listLayer.at(row); } QModelIndex QG_LayerModel::getIndex (RS_Layer * lay) { int row = listLayer.indexOf(lay); if (row<0) return QModelIndex(); return createIndex ( row, NAME); } QVariant QG_LayerModel::data ( const QModelIndex & index, int role ) const { if (!index.isValid() || index.row() >= listLayer.size()) return QVariant(); RS_Layer* layer {listLayer.at(index.row())}; int col {index.column()}; switch (role) { case Qt::DecorationRole: switch (col){ case VISIBLE: if (!layer->isFrozen()) { return layerVisible; } return layerHidden; case LOCKED: if (!layer->isLocked()) { return layerDefreeze; } return layerFreeze; case PRINT: if( !layer->isPrint()) { return layerNoPrint; } return layerPrint; case CONSTRUCTION: if( !layer->isConstruction()) { return layerNoConstruction; } return layerConstruction; default: break; } break; case Qt::DisplayRole: if (NAME == col) { return layer->getName(); } break; case Qt::BackgroundColorRole: if( COLOR_SAMPLE == col) { return layer->getPen().getColor().toQColor(); } break; case Qt::FontRole: if (NAME == col) { if (activeLayer && activeLayer == layer) { QFont font; font.setBold(true); return font; } } break; } return QVariant(); } /** * Constructor. */ QG_LayerWidget::QG_LayerWidget(QG_ActionHandler* ah, QWidget* parent, const char* name, Qt::WindowFlags f) : QWidget(parent, f) { setObjectName(name); actionHandler = ah; layerList = nullptr; showByBlock = false; lastLayer = nullptr; layerModel = new QG_LayerModel(this); layerView = new QTableView(this); layerView->setModel(layerModel); layerView->setShowGrid(true); layerView->setSelectionMode(QAbstractItemView::ExtendedSelection); layerView->setEditTriggers(QAbstractItemView::NoEditTriggers); layerView->setFocusPolicy(Qt::NoFocus); layerView->setMinimumHeight(140); QHeaderView *pHeader {layerView->horizontalHeader()}; pHeader->setMinimumSectionSize( QG_LayerModel::ICONWIDTH + 4); pHeader->setStretchLastSection(true); pHeader->hide(); layerView->setColumnWidth(QG_LayerModel::VISIBLE, QG_LayerModel::ICONWIDTH); layerView->setColumnWidth(QG_LayerModel::VISIBLE, QG_LayerModel::ICONWIDTH); layerView->setColumnWidth(QG_LayerModel::LOCKED, QG_LayerModel::ICONWIDTH); layerView->setColumnWidth(QG_LayerModel::PRINT, QG_LayerModel::ICONWIDTH); layerView->setColumnWidth(QG_LayerModel::CONSTRUCTION, QG_LayerModel::ICONWIDTH); layerView->setColumnWidth(QG_LayerModel::COLOR_SAMPLE, QG_LayerModel::ICONWIDTH); layerView->verticalHeader()->hide(); QVBoxLayout* lay = new QVBoxLayout(this); lay->setContentsMargins(2, 2, 2, 2); QHBoxLayout* layButtons = new QHBoxLayout; QToolButton* but; const QSize minButSize(28,28); // show all layer: but = new QToolButton(this); but->setIcon(QIcon(":/icons/visible.svg")); but->setMinimumSize(minButSize); but->setToolTip(tr("Show all layers")); connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersDefreezeAll); layButtons->addWidget(but); // hide all layer: but = new QToolButton(this); but->setIcon(QIcon(":/icons/invisible.svg")); but->setMinimumSize(minButSize); but->setToolTip(tr("Hide all layers")); connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersFreezeAll); layButtons->addWidget(but); // unlock all layers: but = new QToolButton(this); but->setIcon(QIcon(":/icons/unlocked.svg")); but->setMinimumSize(minButSize); but->setToolTip(tr("Unlock all layers")); connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersUnlockAll); layButtons->addWidget(but); // lock all layers: but = new QToolButton(this); but->setIcon(QIcon(":/icons/locked.svg")); but->setMinimumSize(minButSize); but->setToolTip(tr("Lock all layers")); connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersLockAll); layButtons->addWidget(but); // add layer: but = new QToolButton(this); but->setIcon(QIcon(":/icons/add.svg")); but->setMinimumSize(minButSize); but->setToolTip(tr("Add a layer")); connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersAdd); layButtons->addWidget(but); // remove layer: but = new QToolButton(this); but->setIcon(QIcon(":/icons/remove.svg")); but->setMinimumSize(minButSize); but->setToolTip(tr("Remove layer")); connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersRemove); layButtons->addWidget(but); // rename layer: but = new QToolButton(this); but->setIcon(QIcon(":/icons/rename_active_block.svg")); but->setMinimumSize(minButSize); but->setToolTip(tr("Modify layer attributes / rename")); connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersEdit); layButtons->addWidget(but); // lineEdit to filter layer list with RegEx matchLayerName = new QLineEdit(this); matchLayerName->setReadOnly(false); matchLayerName->setPlaceholderText(tr("Filter")); matchLayerName->setClearButtonEnabled(true); matchLayerName->setToolTip(tr("Looking for matching layer names")); connect(matchLayerName, &QLineEdit::textChanged, this, &QG_LayerWidget::slotUpdateLayerList); lay->addWidget(matchLayerName); lay->addLayout(layButtons); lay->addWidget(layerView); this->setLayout(lay); connect( layerView, &QTableView::clicked, this, &QG_LayerWidget::slotActivated); connect( layerView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QG_LayerWidget::slotSelectionChanged); } /** * Sets the layerlist this layer widget should show. * * @param showByBlock true: show the layer with the name "ByBlock" if * it exists. * false: don't show special layer "ByBlock" */ void QG_LayerWidget::setLayerList(RS_LayerList* layerList, bool showByBlock) { this->layerList = layerList; this->showByBlock = showByBlock; if (layerList != NULL) { this->layerList->setLayerWitget(this); } update(); } void QG_LayerWidget::layerAdded(RS_Layer* layer) { update(); // 1st apply the new layer to the view if (! matchLayerName->text().isEmpty()) { slotUpdateLayerList(); } activateLayer(layer); update(); // update again, if new layer is last row, the height was wrong } /** * @brief getActiveName * @return the name of the active layer */ QString QG_LayerWidget::getActiveName() const { if(layerList){ RS_Layer* p=layerList->getActive(); if(p) return p->getName(); } return QString(); } /** * Updates the layer box from the layers in the graphic. */ void QG_LayerWidget::update() { RS_DEBUG->print("QG_LayerWidget::update() begin"); if (!layerView) { RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::update: nullptr layerView"); return; } int yPos = layerView->verticalScrollBar()->value(); layerView->resizeRowsToContents(); layerView->verticalScrollBar()->setValue(yPos); layerModel->setLayerList(layerList); // allow a null layerList; this clears the widget if (!layerList) { RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::update: nullptr layerList"); return; } RS_DEBUG->print("QG_LayerWidget::update: reactivating current layer"); RS_Layer* activeLayer = layerList->getActive(); if (!activeLayer) { RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::update: nullptr activeLayer"); layerModel->setActiveLayer(nullptr); return; } if (!lastLayer) { RS_DEBUG->print(RS_Debug::D_WARNING, "QG_LayerWidget::update: nullptr lastLayer"); lastLayer = activeLayer; } restoreSelections(); RS_DEBUG->print("QG_LayerWidget::update(): OK"); } void QG_LayerWidget::restoreSelections() { QItemSelectionModel* selectionModel = layerView->selectionModel(); for (auto layer: *layerList) { if (!layer) continue; if (!layer->isVisibleInLayerList()) continue; if (!layer->isSelectedInLayerList()) continue; QModelIndex idx = layerModel->getIndex(layer); QItemSelection selection(idx, idx); selectionModel->select(selection, QItemSelectionModel::Select); } } /** * Activates the given layer and makes it the active * layer in the layerlist. */ void QG_LayerWidget::activateLayer(RS_Layer* layer, bool updateScroll) { RS_DEBUG->print("QG_LayerWidget::activateLayer() begin"); if (!layer || !layerList) { RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::activateLayer: nullptr layer or layerList"); return; } layerList->activate(layer); if (!layerModel) { RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::activateLayer: nullptr layerModel"); return; } QModelIndex idx = layerModel->getIndex(layer); if (!idx.model() || !layerView) { RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::activateLayer: invalid layer or nullptr layerView"); return; } // remember selected status of the layer bool selected = layer->isSelectedInLayerList(); layerView->setCurrentIndex(idx); layerModel->setActiveLayer(layer); layerView->viewport()->update(); // restore selected status of the layer QItemSelectionModel::SelectionFlag selFlag; if (selected) { selFlag = QItemSelectionModel::Select; } else { selFlag = QItemSelectionModel::Deselect; } layer->selectedInLayerList(selected); layerView->selectionModel()->select(QItemSelection(idx, idx), selFlag); if (!updateScroll) { int yPos = layerView->verticalScrollBar()->value(); layerView->verticalScrollBar()->setValue(yPos); } //update active layer name in mainwindow status bar QC_ApplicationWindow::getAppWindow()->slotUpdateActiveLayer(); RS_DEBUG->print("QG_LayerWidget::activateLayer() end"); } /** * Called when the user activates (highlights) a layer. */ void QG_LayerWidget::slotActivated(QModelIndex layerIdx /*const QString& layerName*/) { if (!layerIdx.isValid() || layerList==NULL) { return; } RS_Layer* lay = layerModel->getLayer(layerIdx.row()); if (lay == 0) return; if (layerIdx.column() == QG_LayerModel::NAME) { layerList->activate(lay, true); return; } switch(layerIdx.column()){ case QG_LayerModel::VISIBLE: actionHandler->toggleVisibility(lay); break; case QG_LayerModel::LOCKED: actionHandler->toggleLock(lay); break; case QG_LayerModel::PRINT: actionHandler->togglePrint(lay); break; case QG_LayerModel::CONSTRUCTION: actionHandler->toggleConstruction(lay); break; default: break; } } /** * Called on layers selection/deselection */ void QG_LayerWidget::slotSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected) { QModelIndex index; QItemSelectionModel *selectionModel {layerView->selectionModel()}; foreach (index, selected.indexes()) { auto layer = layerModel->getLayer(index.row()); if (layer) { layer->selectedInLayerList(true); selectionModel->select(QItemSelection(index, index), QItemSelectionModel::Select); } } foreach (index, deselected.indexes()) { auto layer = layerModel->getLayer(index.row()); if (layer && layer->isVisibleInLayerList()) { layer->selectedInLayerList(false); selectionModel->select(QItemSelection(index, index), QItemSelectionModel::Deselect); } } } /** * Called when reg-expresion matchLayerName->text changed */ void QG_LayerWidget::slotUpdateLayerList() { QRegExp rx(""); int pos=0; QString s, n; n=matchLayerName->text(); rx.setPattern(n); rx.setPatternSyntax(QRegExp::WildcardUnix); for (unsigned int i=0; icount() ; i++) { s=layerModel->getLayer(i)->getName(); int f=rx.indexIn(s, pos); if ( !f ) { layerView->showRow(i); layerModel->getLayer(i)->visibleInLayerList(true); } else { layerView->hideRow(i); layerModel->getLayer(i)->visibleInLayerList(false); } } restoreSelections(); } /** * Shows a context menu for the layer widget. Launched with a right click. */ void QG_LayerWidget::contextMenuEvent(QContextMenuEvent *e) { if (actionHandler) { QMenu* contextMenu = new QMenu(this); QLabel* caption = new QLabel(tr("Layer Menu"), this); QPalette palette; palette.setColor(caption->backgroundRole(), RS_Color(0,0,0)); palette.setColor(caption->foregroundRole(), RS_Color(255,255,255)); caption->setPalette(palette); caption->setAlignment( Qt::AlignCenter ); // Actions for all layers: contextMenu->addAction( tr("&Defreeze all Layers"), actionHandler, SLOT(slotLayersDefreezeAll()), 0); contextMenu->addAction( tr("&Freeze all Layers"), actionHandler, SLOT(slotLayersFreezeAll()), 0); contextMenu->addAction( tr("&Unlock all Layers"), actionHandler, SLOT(slotLayersUnlockAll()), 0); contextMenu->addAction( tr("&Lock all Layers"), actionHandler, SLOT(slotLayersLockAll()), 0); contextMenu->addSeparator(); // Actions for selected layers or, // if nothing is selected, for active layer: contextMenu->addAction( tr("Toggle Layer &Visibility"), actionHandler, SLOT(slotLayersToggleView()), 0); contextMenu->addAction( tr("Toggle Layer Loc&k"), actionHandler, SLOT(slotLayersToggleLock()), 0); contextMenu->addAction( tr("Toggle Layer &Printing"), actionHandler, SLOT(slotLayersTogglePrint()), 0); contextMenu->addAction( tr("Toggle &Construction Layer"), actionHandler, SLOT(slotLayersToggleConstruction()), 0); contextMenu->addAction( tr("&Remove Layer"), actionHandler, SLOT(slotLayersRemove()), 0); contextMenu->addSeparator(); // Single layer actions: contextMenu->addAction( tr("&Add Layer"), actionHandler, SLOT(slotLayersAdd()), 0); contextMenu->addAction( tr("Edit Layer &Attributes"), actionHandler, SLOT(slotLayersEdit()), 0); contextMenu->exec(QCursor::pos()); delete contextMenu; } e->accept(); } /** * Escape releases focus. */ void QG_LayerWidget::keyPressEvent(QKeyEvent* e) { switch (e->key()) { case Qt::Key_Escape: emit escape(); break; default: QWidget::keyPressEvent(e); break; } } void QG_LayerWidget::activateLayer(int row) { auto layer = layerModel->getLayer(row); if (layer) layerList->activate(layer, true); else qWarning("activateLayer: row %d doesn't exist", row); }