1 /****************************************************************************
2 **
3 ** This file is part of the LibreCAD project, a 2D CAD program
4 **
5 ** Copyright (C) 2015 A. Stebich (librecad@mail.lordofbikes.de)
6 ** Copyright (C) 2011 Rallaz (rallazz@gmail.com)
7 ** Copyright (C) 2010-2011 R. van Twisk (librecad@rvt.dds.nl)
8 **
9 **
10 ** This file is free software; you can redistribute it and/or modify
11 ** it under the terms of the GNU General Public License as published by
12 ** the Free Software Foundation; either version 2 of the License, or
13 ** (at your option) any later version.
14 **
15 ** This program is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ** GNU General Public License for more details.
19 **
20 ** You should have received a copy of the GNU General Public License
21 ** along with this program; if not, write to the Free Software
22 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 **
24 ** This copyright notice MUST APPEAR in all copies of the script!
25 **
26 **********************************************************************/
27
28 #include "qg_layerwidget.h"
29 #include "qg_actionhandler.h"
30 #include "qc_applicationwindow.h"
31
32 #include <QBitmap>
33 #include <QScrollBar>
34 #include <QTableView>
35 #include <QHeaderView>
36 #include <QToolButton>
37 #include <QMenu>
38 #include <QBoxLayout>
39 #include <QLabel>
40 #include <QLineEdit>
41 #include <QContextMenuEvent>
42 #include <QKeyEvent>
43 #include "rs_debug.h"
44
QG_LayerModel(QObject * parent)45 QG_LayerModel::QG_LayerModel(QObject * parent) : QAbstractTableModel(parent) {
46 layerVisible = QIcon(":/icons/visible.svg");
47 layerHidden = QIcon(":/icons/invisible.svg");
48 layerDefreeze = QIcon(":/icons/unlocked.svg");
49 layerFreeze = QIcon(":/icons/locked.svg");
50 layerPrint = QIcon(":/icons/print.svg");
51 layerNoPrint = QIcon(":/icons/noprint.svg");
52 layerConstruction = QIcon(":/icons/construction_layer.svg");
53 layerNoConstruction = QIcon(":/icons/noconstruction.svg");
54 }
55
56
57
rowCount(const QModelIndex &) const58 int QG_LayerModel::rowCount ( const QModelIndex & /*parent*/ ) const {
59 return listLayer.size();
60 }
61
62
63
parent(const QModelIndex &) const64 QModelIndex QG_LayerModel::parent ( const QModelIndex & /*index*/ ) const {
65 return QModelIndex();
66 }
67
68
69
index(int row,int column,const QModelIndex &) const70 QModelIndex QG_LayerModel::index ( int row, int column, const QModelIndex & /*parent*/ ) const {
71 if ( row >= listLayer.size() || row < 0)
72 return QModelIndex();
73 return createIndex ( row, column);
74 }
75
76
77
setLayerList(RS_LayerList * ll)78 void QG_LayerModel::setLayerList(RS_LayerList* ll) {
79 /* since 4.6 the recommended way is to use begin/endResetModel()
80 * TNick <nicu.tofan@gmail.com>
81 */
82 beginResetModel();
83 listLayer.clear();
84 if (ll == NULL) {
85 endResetModel();
86 return;
87 }
88 for (unsigned i=0; i < ll->count(); ++i) {
89 listLayer.append(ll->at(i));
90 }
91 setActiveLayer(ll->getActive());
92 std::sort( listLayer.begin(), listLayer.end(), [](const RS_Layer *s1, const RS_Layer *s2)-> bool{
93 return s1->getName() < s2->getName();
94 } );
95
96 //called to force redraw
97 endResetModel();
98 }
99
100
101
getLayer(int row)102 RS_Layer *QG_LayerModel::getLayer( int row ) {
103 if ( row >= listLayer.size() || row < 0)
104 return NULL;
105 return listLayer.at(row);
106 }
107
108
109
getIndex(RS_Layer * lay)110 QModelIndex QG_LayerModel::getIndex (RS_Layer * lay) {
111 int row = listLayer.indexOf(lay);
112 if (row<0)
113 return QModelIndex();
114 return createIndex ( row, NAME);
115 }
116
117
118
data(const QModelIndex & index,int role) const119 QVariant QG_LayerModel::data ( const QModelIndex & index, int role ) const {
120 if (!index.isValid() || index.row() >= listLayer.size())
121 return QVariant();
122
123 RS_Layer* layer {listLayer.at(index.row())};
124 int col {index.column()};
125
126 switch (role) {
127 case Qt::DecorationRole:
128 switch (col){
129 case VISIBLE:
130 if (!layer->isFrozen()) {
131 return layerVisible;
132 }
133 return layerHidden;
134
135 case LOCKED:
136 if (!layer->isLocked()) {
137 return layerDefreeze;
138 }
139 return layerFreeze;
140
141 case PRINT:
142 if( !layer->isPrint()) {
143 return layerNoPrint;
144 }
145 return layerPrint;
146
147 case CONSTRUCTION:
148 if( !layer->isConstruction()) {
149 return layerNoConstruction;
150 }
151 return layerConstruction;
152
153 default:
154 break;
155 }
156 break;
157
158 case Qt::DisplayRole:
159 if (NAME == col) {
160 return layer->getName();
161 }
162 break;
163
164 case Qt::BackgroundColorRole:
165 if( COLOR_SAMPLE == col) {
166 return layer->getPen().getColor().toQColor();
167 }
168 break;
169
170 case Qt::FontRole:
171 if (NAME == col) {
172 if (activeLayer && activeLayer == layer) {
173 QFont font;
174 font.setBold(true);
175 return font;
176 }
177 }
178 break;
179 }
180
181 return QVariant();
182 }
183
184
185
186 /**
187 * Constructor.
188 */
QG_LayerWidget(QG_ActionHandler * ah,QWidget * parent,const char * name,Qt::WindowFlags f)189 QG_LayerWidget::QG_LayerWidget(QG_ActionHandler* ah, QWidget* parent,
190 const char* name, Qt::WindowFlags f)
191 : QWidget(parent, f) {
192
193 setObjectName(name);
194 actionHandler = ah;
195 layerList = nullptr;
196 showByBlock = false;
197 lastLayer = nullptr;
198
199 layerModel = new QG_LayerModel(this);
200 layerView = new QTableView(this);
201 layerView->setModel(layerModel);
202 layerView->setShowGrid(true);
203 layerView->setSelectionMode(QAbstractItemView::ExtendedSelection);
204 layerView->setEditTriggers(QAbstractItemView::NoEditTriggers);
205 layerView->setFocusPolicy(Qt::NoFocus);
206 layerView->setMinimumHeight(140);
207 QHeaderView *pHeader {layerView->horizontalHeader()};
208 pHeader->setMinimumSectionSize( QG_LayerModel::ICONWIDTH + 4);
209 pHeader->setStretchLastSection(true);
210 pHeader->hide();
211 layerView->setColumnWidth(QG_LayerModel::VISIBLE, QG_LayerModel::ICONWIDTH);
212 layerView->setColumnWidth(QG_LayerModel::VISIBLE, QG_LayerModel::ICONWIDTH);
213 layerView->setColumnWidth(QG_LayerModel::LOCKED, QG_LayerModel::ICONWIDTH);
214 layerView->setColumnWidth(QG_LayerModel::PRINT, QG_LayerModel::ICONWIDTH);
215 layerView->setColumnWidth(QG_LayerModel::CONSTRUCTION, QG_LayerModel::ICONWIDTH);
216 layerView->setColumnWidth(QG_LayerModel::COLOR_SAMPLE, QG_LayerModel::ICONWIDTH);
217 layerView->verticalHeader()->hide();
218
219 QVBoxLayout* lay = new QVBoxLayout(this);
220 lay->setContentsMargins(2, 2, 2, 2);
221
222 QHBoxLayout* layButtons = new QHBoxLayout;
223 QToolButton* but;
224 const QSize minButSize(28,28);
225 // show all layer:
226 but = new QToolButton(this);
227 but->setIcon(QIcon(":/icons/visible.svg"));
228 but->setMinimumSize(minButSize);
229 but->setToolTip(tr("Show all layers"));
230 connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersDefreezeAll);
231 layButtons->addWidget(but);
232 // hide all layer:
233 but = new QToolButton(this);
234 but->setIcon(QIcon(":/icons/invisible.svg"));
235 but->setMinimumSize(minButSize);
236 but->setToolTip(tr("Hide all layers"));
237 connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersFreezeAll);
238 layButtons->addWidget(but);
239 // unlock all layers:
240 but = new QToolButton(this);
241 but->setIcon(QIcon(":/icons/unlocked.svg"));
242 but->setMinimumSize(minButSize);
243 but->setToolTip(tr("Unlock all layers"));
244 connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersUnlockAll);
245 layButtons->addWidget(but);
246 // lock all layers:
247 but = new QToolButton(this);
248 but->setIcon(QIcon(":/icons/locked.svg"));
249 but->setMinimumSize(minButSize);
250 but->setToolTip(tr("Lock all layers"));
251 connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersLockAll);
252 layButtons->addWidget(but);
253 // add layer:
254 but = new QToolButton(this);
255 but->setIcon(QIcon(":/icons/add.svg"));
256 but->setMinimumSize(minButSize);
257 but->setToolTip(tr("Add a layer"));
258 connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersAdd);
259 layButtons->addWidget(but);
260 // remove layer:
261 but = new QToolButton(this);
262 but->setIcon(QIcon(":/icons/remove.svg"));
263 but->setMinimumSize(minButSize);
264 but->setToolTip(tr("Remove layer"));
265 connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersRemove);
266 layButtons->addWidget(but);
267 // rename layer:
268 but = new QToolButton(this);
269 but->setIcon(QIcon(":/icons/rename_active_block.svg"));
270 but->setMinimumSize(minButSize);
271 but->setToolTip(tr("Modify layer attributes / rename"));
272 connect(but, &QToolButton::clicked, actionHandler, &QG_ActionHandler::slotLayersEdit);
273 layButtons->addWidget(but);
274
275 // lineEdit to filter layer list with RegEx
276 matchLayerName = new QLineEdit(this);
277 matchLayerName->setReadOnly(false);
278 matchLayerName->setPlaceholderText(tr("Filter"));
279 matchLayerName->setClearButtonEnabled(true);
280 matchLayerName->setToolTip(tr("Looking for matching layer names"));
281 connect(matchLayerName, &QLineEdit::textChanged, this, &QG_LayerWidget::slotUpdateLayerList);
282
283 lay->addWidget(matchLayerName);
284 lay->addLayout(layButtons);
285 lay->addWidget(layerView);
286 this->setLayout(lay);
287
288 connect( layerView, &QTableView::clicked, this, &QG_LayerWidget::slotActivated);
289 connect( layerView->selectionModel(), &QItemSelectionModel::selectionChanged,
290 this, &QG_LayerWidget::slotSelectionChanged);
291 }
292
293
294
295 /**
296 * Sets the layerlist this layer widget should show.
297 *
298 * @param showByBlock true: show the layer with the name "ByBlock" if
299 * it exists.
300 * false: don't show special layer "ByBlock"
301 */
setLayerList(RS_LayerList * layerList,bool showByBlock)302 void QG_LayerWidget::setLayerList(RS_LayerList* layerList, bool showByBlock) {
303 this->layerList = layerList;
304 this->showByBlock = showByBlock;
305 if (layerList != NULL) {
306 this->layerList->setLayerWitget(this);
307 }
308 update();
309 }
310
311
312
layerAdded(RS_Layer * layer)313 void QG_LayerWidget::layerAdded(RS_Layer* layer)
314 {
315 update(); // 1st apply the new layer to the view
316 if (! matchLayerName->text().isEmpty()) {
317 slotUpdateLayerList();
318 }
319 activateLayer(layer);
320 update(); // update again, if new layer is last row, the height was wrong
321 }
322
323
324
325 /**
326 * @brief getActiveName
327 * @return the name of the active layer
328 */
getActiveName() const329 QString QG_LayerWidget::getActiveName() const
330 {
331 if(layerList){
332 RS_Layer* p=layerList->getActive();
333 if(p) return p->getName();
334 }
335 return QString();
336 }
337
338
339
340 /**
341 * Updates the layer box from the layers in the graphic.
342 */
update()343 void QG_LayerWidget::update() {
344
345 RS_DEBUG->print("QG_LayerWidget::update() begin");
346
347 if (!layerView) {
348 RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::update: nullptr layerView");
349 return;
350 }
351 int yPos = layerView->verticalScrollBar()->value();
352 layerView->resizeRowsToContents();
353 layerView->verticalScrollBar()->setValue(yPos);
354
355 layerModel->setLayerList(layerList); // allow a null layerList; this clears the widget
356
357 if (!layerList) {
358 RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::update: nullptr layerList");
359 return;
360 }
361
362 RS_DEBUG->print("QG_LayerWidget::update: reactivating current layer");
363
364 RS_Layer* activeLayer = layerList->getActive();
365 if (!activeLayer) {
366 RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::update: nullptr activeLayer");
367 layerModel->setActiveLayer(nullptr);
368 return;
369 }
370 activateLayer(activeLayer);
371
372 if (!lastLayer) {
373 RS_DEBUG->print(RS_Debug::D_WARNING, "QG_LayerWidget::update: nullptr lastLayer");
374 lastLayer = activeLayer;
375 }
376
377 restoreSelections();
378
379 RS_DEBUG->print("QG_LayerWidget::update(): OK");
380 }
381
382
restoreSelections()383 void QG_LayerWidget::restoreSelections() {
384
385 QItemSelectionModel* selectionModel = layerView->selectionModel();
386
387 for (auto layer: *layerList) {
388 if (!layer) continue;
389 if (!layer->isVisibleInLayerList()) continue;
390 if (!layer->isSelectedInLayerList()) continue;
391
392 QModelIndex idx = layerModel->getIndex(layer);
393 QItemSelection selection(idx, idx);
394 selectionModel->select(selection, QItemSelectionModel::Select);
395 }
396 }
397
398
399 /**
400 * Activates the given layer and makes it the active
401 * layer in the layerlist.
402 */
activateLayer(RS_Layer * layer,bool updateScroll)403 void QG_LayerWidget::activateLayer(RS_Layer* layer, bool updateScroll) {
404 RS_DEBUG->print("QG_LayerWidget::activateLayer() begin");
405
406 if (!layer || !layerList) {
407 RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::activateLayer: nullptr layer or layerList");
408 return;
409 }
410
411 layerList->activate(layer);
412
413 if (!layerModel) {
414 RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::activateLayer: nullptr layerModel");
415 return;
416 }
417 QModelIndex idx = layerModel->getIndex(layer);
418
419 if (!idx.model() || !layerView) {
420 RS_DEBUG->print(RS_Debug::D_ERROR, "QG_LayerWidget::activateLayer: invalid layer or nullptr layerView");
421 return;
422 }
423
424 // remember selected status of the layer
425 bool selected = layer->isSelectedInLayerList();
426
427 layerView->setCurrentIndex(idx);
428 layerModel->setActiveLayer(layer);
429 layerView->viewport()->update();
430
431 // restore selected status of the layer
432 QItemSelectionModel::SelectionFlag selFlag;
433 if (selected) {
434 selFlag = QItemSelectionModel::Select;
435 } else {
436 selFlag = QItemSelectionModel::Deselect;
437 }
438 layer->selectedInLayerList(selected);
439 layerView->selectionModel()->select(QItemSelection(idx, idx), selFlag);
440
441 if (!updateScroll) {
442 int yPos = layerView->verticalScrollBar()->value();
443 layerView->verticalScrollBar()->setValue(yPos);
444 }
445
446 //update active layer name in mainwindow status bar
447 QC_ApplicationWindow::getAppWindow()->slotUpdateActiveLayer();
448
449 RS_DEBUG->print("QG_LayerWidget::activateLayer() end");
450 }
451
452
453
454 /**
455 * Called when the user activates (highlights) a layer.
456 */
slotActivated(QModelIndex layerIdx)457 void QG_LayerWidget::slotActivated(QModelIndex layerIdx /*const QString& layerName*/) {
458 if (!layerIdx.isValid() || layerList==NULL) {
459 return;
460 }
461
462 RS_Layer* lay = layerModel->getLayer(layerIdx.row());
463 if (lay == 0)
464 return;
465
466 if (layerIdx.column() == QG_LayerModel::NAME) {
467 layerList->activate(lay, true);
468 return;
469 }
470
471 switch(layerIdx.column()){
472 case QG_LayerModel::VISIBLE:
473 actionHandler->toggleVisibility(lay);
474 break;
475 case QG_LayerModel::LOCKED:
476 actionHandler->toggleLock(lay);
477 break;
478 case QG_LayerModel::PRINT:
479 actionHandler->togglePrint(lay);
480 break;
481 case QG_LayerModel::CONSTRUCTION:
482 actionHandler->toggleConstruction(lay);
483 break;
484 default:
485 break;
486 }
487 }
488
489
490 /**
491 * Called on layers selection/deselection
492 */
slotSelectionChanged(const QItemSelection & selected,const QItemSelection & deselected)493 void QG_LayerWidget::slotSelectionChanged(
494 const QItemSelection &selected,
495 const QItemSelection &deselected)
496 {
497 QModelIndex index;
498 QItemSelectionModel *selectionModel {layerView->selectionModel()};
499
500 foreach (index, selected.indexes()) {
501 auto layer = layerModel->getLayer(index.row());
502 if (layer) {
503 layer->selectedInLayerList(true);
504 selectionModel->select(QItemSelection(index, index), QItemSelectionModel::Select);
505 }
506 }
507
508 foreach (index, deselected.indexes()) {
509 auto layer = layerModel->getLayer(index.row());
510 if (layer && layer->isVisibleInLayerList()) {
511 layer->selectedInLayerList(false);
512 selectionModel->select(QItemSelection(index, index), QItemSelectionModel::Deselect);
513 }
514 }
515 }
516
517
518 /**
519 * Called when reg-expresion matchLayerName->text changed
520 */
slotUpdateLayerList()521 void QG_LayerWidget::slotUpdateLayerList() {
522 QRegExp rx("");
523 int pos=0;
524 QString s, n;
525
526 n=matchLayerName->text();
527 rx.setPattern(n);
528 rx.setPatternSyntax(QRegExp::WildcardUnix);
529
530 for (unsigned int i=0; i<layerList->count() ; i++) {
531 s=layerModel->getLayer(i)->getName();
532 int f=rx.indexIn(s, pos);
533 if ( !f ) {
534 layerView->showRow(i);
535 layerModel->getLayer(i)->visibleInLayerList(true);
536 } else {
537 layerView->hideRow(i);
538 layerModel->getLayer(i)->visibleInLayerList(false);
539 }
540 }
541
542 restoreSelections();
543 }
544
545 /**
546 * Shows a context menu for the layer widget. Launched with a right click.
547 */
contextMenuEvent(QContextMenuEvent * e)548 void QG_LayerWidget::contextMenuEvent(QContextMenuEvent *e) {
549
550 if (actionHandler) {
551 QMenu* contextMenu = new QMenu(this);
552 QLabel* caption = new QLabel(tr("Layer Menu"), this);
553 QPalette palette;
554 palette.setColor(caption->backgroundRole(), RS_Color(0,0,0));
555 palette.setColor(caption->foregroundRole(), RS_Color(255,255,255));
556 caption->setPalette(palette);
557 caption->setAlignment( Qt::AlignCenter );
558 // Actions for all layers:
559 contextMenu->addAction( tr("&Defreeze all Layers"), actionHandler,
560 SLOT(slotLayersDefreezeAll()), 0);
561 contextMenu->addAction( tr("&Freeze all Layers"), actionHandler,
562 SLOT(slotLayersFreezeAll()), 0);
563 contextMenu->addAction( tr("&Unlock all Layers"), actionHandler,
564 SLOT(slotLayersUnlockAll()), 0);
565 contextMenu->addAction( tr("&Lock all Layers"), actionHandler,
566 SLOT(slotLayersLockAll()), 0);
567 contextMenu->addSeparator();
568 // Actions for selected layers or,
569 // if nothing is selected, for active layer:
570 contextMenu->addAction( tr("Toggle Layer &Visibility"), actionHandler,
571 SLOT(slotLayersToggleView()), 0);
572 contextMenu->addAction( tr("Toggle Layer Loc&k"), actionHandler,
573 SLOT(slotLayersToggleLock()), 0);
574 contextMenu->addAction( tr("Toggle Layer &Printing"), actionHandler,
575 SLOT(slotLayersTogglePrint()), 0);
576 contextMenu->addAction( tr("Toggle &Construction Layer"), actionHandler,
577 SLOT(slotLayersToggleConstruction()), 0);
578 contextMenu->addAction( tr("&Remove Layer"), actionHandler,
579 SLOT(slotLayersRemove()), 0);
580 contextMenu->addSeparator();
581 // Single layer actions:
582 contextMenu->addAction( tr("&Add Layer"), actionHandler,
583 SLOT(slotLayersAdd()), 0);
584 contextMenu->addAction( tr("Edit Layer &Attributes"), actionHandler,
585 SLOT(slotLayersEdit()), 0);
586 contextMenu->exec(QCursor::pos());
587 delete contextMenu;
588 }
589
590 e->accept();
591 }
592
593
594 /**
595 * Escape releases focus.
596 */
keyPressEvent(QKeyEvent * e)597 void QG_LayerWidget::keyPressEvent(QKeyEvent* e) {
598 switch (e->key()) {
599
600 case Qt::Key_Escape:
601 emit escape();
602 break;
603
604 default:
605 QWidget::keyPressEvent(e);
606 break;
607 }
608 }
609
610
activateLayer(int row)611 void QG_LayerWidget::activateLayer(int row)
612 {
613 auto layer = layerModel->getLayer(row);
614 if (layer)
615 layerList->activate(layer, true);
616 else
617 qWarning("activateLayer: row %d doesn't exist", row);
618 }
619