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