Files
newspark110/ui/qg_blockwidget.cpp
Chenwenxuan edac2715f0 init
2024-03-06 14:54:30 +08:00

495 lines
15 KiB
C++

/****************************************************************************
**
** This file is part of the LibreCAD project, a 2D CAD program
**
** Copyright (C) 2020 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 <QScrollBar>
#include <QTableView>
#include <QHeaderView>
#include <QToolButton>
#include <QMenu>
#include <QBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QContextMenuEvent>
#include <algorithm>
#include "qg_blockwidget.h"
#include "rs_blocklist.h"
#include "qg_actionhandler.h"
#include "rs_debug.h"
QG_BlockModel::QG_BlockModel(QObject * parent) : QAbstractTableModel(parent) {
blockVisible = QIcon(":/icons/visible.svg");
blockHidden = QIcon(":/icons/invisible.svg");
}
int QG_BlockModel::rowCount ( const QModelIndex & /*parent*/ ) const {
return listBlock.size();
}
QModelIndex QG_BlockModel::parent ( const QModelIndex & /*index*/ ) const {
return QModelIndex();
}
QModelIndex QG_BlockModel::index ( int row, int column, const QModelIndex & /*parent*/ ) const {
if ( row >= listBlock.size() || row < 0)
return QModelIndex();
return createIndex ( row, column);
}
bool blockLessThan(const RS_Block *s1, const RS_Block *s2) {
return s1->getName() < s2->getName();
}
void QG_BlockModel::setBlockList(RS_BlockList* bl) {
/* since 4.6 the recommended way is to use begin/endResetModel()
* TNick <nicu.tofan@gmail.com>
*/
beginResetModel();
listBlock.clear();
if (bl == NULL){
endResetModel();
return;
}
for (int i=0; i<bl->count(); ++i) {
if ( !bl->at(i)->isUndone() )
listBlock.append(bl->at(i));
}
setActiveBlock(bl->getActive());
std::sort( listBlock.begin(), listBlock.end(), blockLessThan);
//called to force redraw
endResetModel();
}
RS_Block *QG_BlockModel::getBlock( int row ){
if ( row >= listBlock.size() || row < 0)
return NULL;
return listBlock.at(row);
}
QModelIndex QG_BlockModel::getIndex (RS_Block * blk){
int row = listBlock.indexOf(blk);
if (row<0)
return QModelIndex();
return createIndex ( row, NAME);
}
QVariant QG_BlockModel::data ( const QModelIndex & index, int role ) const {
if (!index.isValid() || index.row() >= listBlock.size())
return QVariant();
RS_Block* blk = listBlock.at(index.row());
if (role ==Qt::DecorationRole && index.column() == VISIBLE) {
if (!blk->isFrozen()) {
return blockVisible;
} else {
return blockHidden;
}
}
if (role ==Qt::DisplayRole && index.column() == NAME) {
return blk->getName();
}
if (role == Qt::FontRole && index.column() == NAME) {
if (activeBlock && activeBlock == blk) {
QFont font;
font.setBold(true);
return font;
}
}
//Other roles:
return QVariant();
}
/**
* Constructor.
*/
QG_BlockWidget::QG_BlockWidget(QG_ActionHandler* ah, QWidget* parent,
const char* name, Qt::WindowFlags f)
: QWidget(parent, f) {
setObjectName(name);
actionHandler = ah;
blockList = NULL;
lastBlock = NULL;
blockModel = new QG_BlockModel;
blockView = new QTableView(this);
blockView->setModel (blockModel);
blockView->setShowGrid (false);
blockView->setSelectionMode(QAbstractItemView::ExtendedSelection);
blockView->setEditTriggers(QAbstractItemView::NoEditTriggers);
blockView->setFocusPolicy(Qt::NoFocus);
blockView->setColumnWidth(QG_BlockModel::VISIBLE, 20);
blockView->verticalHeader()->hide();
blockView->horizontalHeader()->setStretchLastSection(true);
blockView->horizontalHeader()->hide();
QVBoxLayout* lay = new QVBoxLayout(this);
lay->setSpacing ( 0 );
lay->setContentsMargins(2, 2, 2, 2);
QHBoxLayout* layButtons = new QHBoxLayout();
QHBoxLayout* layButtons2 = new QHBoxLayout();
QToolButton* but;
const QSize button_size(28,28);
// show all blocks:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/visible.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("Show all blocks"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksDefreezeAll);
layButtons->addWidget(but);
// hide all blocks:
but = new QToolButton(this);
but->setIcon( QIcon(":/icons/invisible.svg") );
but->setMinimumSize(button_size);
but->setToolTip(tr("Hide all blocks"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksFreezeAll);
layButtons->addWidget(but);
// create block:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/create_block.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("Create Block"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksCreate);
layButtons->addWidget(but);
// add block:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/add.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("Add an empty block"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksAdd);
layButtons->addWidget(but);
// remove block:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/remove.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("Remove block"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksRemove);
layButtons->addWidget(but);
// edit attributes:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/rename_active_block.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("Rename the active block"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksAttributes);
layButtons2->addWidget(but);
// edit block:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/properties.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("Edit the active block\n"
"in a separate window"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksEdit);
layButtons2->addWidget(but);
// save block:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/save.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("save the active block to a file"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksSave);
layButtons2->addWidget(but);
// insert block:
but = new QToolButton(this);
but->setIcon(QIcon(":/icons/insert_active_block.svg"));
but->setMinimumSize(button_size);
but->setToolTip(tr("Insert the active block"));
connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotBlocksInsert);
layButtons2->addWidget(but);
// lineEdit to filter block list with RegEx
matchBlockName = new QLineEdit(this);
matchBlockName->setReadOnly(false);
matchBlockName->setPlaceholderText(tr("Filter"));
matchBlockName->setClearButtonEnabled(true);
matchBlockName->setToolTip(tr("Looking for matching block names"));
connect(matchBlockName, &QLineEdit::textChanged, this, &QG_BlockWidget::slotUpdateBlockList);
lay->addWidget(matchBlockName);
lay->addLayout(layButtons);
lay->addLayout(layButtons2);
lay->addWidget(blockView);
connect(blockView, &QTableView::clicked, this, &QG_BlockWidget::slotActivated);
connect(blockView->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QG_BlockWidget::slotSelectionChanged);
}
/**
* Destructor
*/
QG_BlockWidget::~QG_BlockWidget() {
delete blockView;
delete blockModel;
}
/**
* Updates the block box from the blocks in the graphic.
*/
void QG_BlockWidget::update() {
RS_DEBUG->print("QG_BlockWidget::update()");
RS_Block* activeBlock;
if (blockList) {
activeBlock = blockList->getActive();
} else {
activeBlock = NULL;
}
blockModel->setBlockList(blockList);
if (blockList==NULL) {
RS_DEBUG->print("QG_BlockWidget::update(): blockList is NULL");
blockModel->setActiveBlock(nullptr);
return;
}
RS_Block* b = lastBlock;
activateBlock(activeBlock);
lastBlock = b;
blockView->resizeRowsToContents();
restoreSelections();
RS_DEBUG->print("QG_BlockWidget::update() done");
}
void QG_BlockWidget::restoreSelections() {
QItemSelectionModel* selectionModel = blockView->selectionModel();
for (auto block: *blockList) {
if (!block) continue;
if (!block->isVisibleInBlockList()) continue;
if (!block->isSelectedInBlockList()) continue;
QModelIndex idx = blockModel->getIndex(block);
QItemSelection selection(idx, idx);
selectionModel->select(selection, QItemSelectionModel::Select);
}
}
/**
* Activates the given block and makes it the active
* block in the blocklist.
*/
void QG_BlockWidget::activateBlock(RS_Block* block) {
RS_DEBUG->print("QG_BlockWidget::activateBlock()");
if (block==NULL || blockList==NULL) {
return;
}
lastBlock = blockList->getActive();
blockList->activate(block);
int yPos = blockView->verticalScrollBar()->value();
QModelIndex idx = blockModel->getIndex (block);
// remember selected status of the block
bool selected = block->isSelectedInBlockList();
blockView->setCurrentIndex ( idx );
blockModel->setActiveBlock(block);
blockView->viewport()->update();
// restore selected status of the block
QItemSelectionModel::SelectionFlag selFlag;
if (selected) {
selFlag = QItemSelectionModel::Select;
} else {
selFlag = QItemSelectionModel::Deselect;
}
block->selectedInBlockList(selected);
blockView->selectionModel()->select(QItemSelection(idx, idx), selFlag);
blockView->verticalScrollBar()->setValue(yPos);
}
/**
* Called when the user activates (highlights) a block.
*/
void QG_BlockWidget::slotActivated(QModelIndex blockIdx) {
if (!blockIdx.isValid() || blockList==NULL)
return;
RS_Block * block = blockModel->getBlock( blockIdx.row() );
if (block == 0)
return;
if (blockIdx.column() == QG_BlockModel::VISIBLE) {
RS_Block* b = blockList->getActive();
blockList->activate(block);
actionHandler->slotBlocksToggleView();
activateBlock(b);
return;
}
if (blockIdx.column() == QG_BlockModel::NAME) {
lastBlock = blockList->getActive();
activateBlock(block);
}
}
/**
* Called on blocks selection/deselection
*/
void QG_BlockWidget::slotSelectionChanged(
const QItemSelection &selected,
const QItemSelection &deselected)
{
QModelIndex index;
QItemSelectionModel *selectionModel {blockView->selectionModel()};
foreach (index, selected.indexes()) {
auto block = blockModel->getBlock(index.row());
if (block) {
block->selectedInBlockList(true);
selectionModel->select(QItemSelection(index, index), QItemSelectionModel::Select);
}
}
foreach (index, deselected.indexes()) {
auto block = blockModel->getBlock(index.row());
if (block && block->isVisibleInBlockList()) {
block->selectedInBlockList(false);
selectionModel->select(QItemSelection(index, index), QItemSelectionModel::Deselect);
}
}
}
/**
* Shows a context menu for the block widget. Launched with a right click.
*/
void QG_BlockWidget::contextMenuEvent(QContextMenuEvent *e) {
// select item (block) in Block List widget first because left-mouse-click event are not to be triggered
// slotActivated(blockView->currentIndex());
QMenu* contextMenu = new QMenu(this);
QLabel* caption = new QLabel(tr("Block 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 blocks:
contextMenu->addAction( tr("&Defreeze all Blocks"), actionHandler,
SLOT(slotBlocksDefreezeAll()), 0);
contextMenu->addAction( tr("&Freeze all Blocks"), actionHandler,
SLOT(slotBlocksFreezeAll()), 0);
contextMenu->addSeparator();
// Actions for selected blocks or,
// if nothing is selected, for active block:
contextMenu->addAction( tr("&Toggle Visibility"), actionHandler,
SLOT(slotBlocksToggleView()), 0);
contextMenu->addAction( tr("&Remove Block"), actionHandler,
SLOT(slotBlocksRemove()), 0);
contextMenu->addSeparator();
// Single block actions:
contextMenu->addAction( tr("&Add Block"), actionHandler,
SLOT(slotBlocksAdd()), 0);
contextMenu->addAction( tr("&Rename Block"), actionHandler,
SLOT(slotBlocksAttributes()), 0);
contextMenu->addAction( tr("&Edit Block"), actionHandler,
SLOT(slotBlocksEdit()), 0);
contextMenu->addAction( tr("&Insert Block"), actionHandler,
SLOT(slotBlocksInsert()), 0);
contextMenu->addAction( tr("&Create New Block"), actionHandler,
SLOT(slotBlocksCreate()), 0);
contextMenu->exec(QCursor::pos());
delete contextMenu;
e->accept();
}
/**
* Escape releases focus.
*/
void QG_BlockWidget::keyPressEvent(QKeyEvent* e) {
switch (e->key()) {
case Qt::Key_Escape:
emit escape();
break;
default:
QWidget::keyPressEvent(e);
break;
}
}
void QG_BlockWidget::blockAdded(RS_Block*) {
update();
if (! matchBlockName->text().isEmpty()) {
slotUpdateBlockList();
}
}
/**
* Called when reg-expression matchBlockName->text changed
*/
void QG_BlockWidget::slotUpdateBlockList() {
if (!blockList) {
return;
}
QRegExp rx("");
int pos = 0;
QString s, n;
n = matchBlockName->text();
rx.setPattern(n);
rx.setPatternSyntax(QRegExp::WildcardUnix);
for (int i = 0; i < blockList->count(); i++) {
RS_Block* block = blockModel->getBlock(i);
if (!block) continue;
s = block->getName();
int f = rx.indexIn(s, pos);
if (!f) {
blockView->showRow(i);
blockModel->getBlock(i)->visibleInBlockList(true);
} else {
blockView->hideRow(i);
blockModel->getBlock(i)->visibleInBlockList(false);
}
}
restoreSelections();
}