1 /************************************************************************
2  **
3  **  @file   vwidgetdetails.cpp
4  **  @author Roman Telezhynskyi <dismine(at)gmail.com>
5  **  @date   25 6, 2016
6  **
7  **  @brief
8  **  @copyright
9  **  This source code is part of the Valentina project, a pattern making
10  **  program, whose allow create and modeling patterns of clothing.
11  **  Copyright (C) 2016 Valentina project
12  **  <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13  **
14  **  Valentina is free software: you can redistribute it and/or modify
15  **  it under the terms of the GNU General Public License as published by
16  **  the Free Software Foundation, either version 3 of the License, or
17  **  (at your option) any later version.
18  **
19  **  Valentina is distributed in the hope that it will be useful,
20  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  **  GNU General Public License for more details.
23  **
24  **  You should have received a copy of the GNU General Public License
25  **  along with Valentina.  If not, see <http://www.gnu.org/licenses/>.
26  **
27  *************************************************************************/
28 
29 #include "vwidgetdetails.h"
30 #include "ui_vwidgetdetails.h"
31 #include "../ifc/xml/vabstractpattern.h"
32 #include "../vpatterndb/vcontainer.h"
33 #include "../vmisc/vabstractapplication.h"
34 #include "../vtools/undocommands/togglepiecestate.h"
35 #include "../vtools/tools/vtoolseamallowance.h"
36 
37 #include <QMenu>
38 #include <QTimer>
39 #include <QUndoStack>
40 
41 namespace
42 {
43     enum PieceColumn
44     {
45         InLayout = 0,
46         PieceName = 1
47     };
48 
49     Q_GLOBAL_STATIC_WITH_ARGS(const QString, allowDetailIcon, (QLatin1String("://icon/16x16/allow_detail.png")))
50     Q_GLOBAL_STATIC_WITH_ARGS(const QString, forbidDetailIcon, (QLatin1String("://icon/16x16/forbid_detail.png")))
51 }
52 
53 //---------------------------------------------------------------------------------------------------------------------
VWidgetDetails(VContainer * data,VAbstractPattern * doc,QWidget * parent)54 VWidgetDetails::VWidgetDetails(VContainer *data, VAbstractPattern *doc, QWidget *parent)
55     : QWidget(parent),
56       ui(new Ui::VWidgetDetails),
57       m_doc(doc),
58       m_data(data),
59       m_updateListTimer(new QTimer(this))
60 {
61     ui->setupUi(this);
62 
63     ui->checkBoxHideNotInLayout->setChecked(false);
64 
65     FillTable(m_data->DataPieces());
66 
67     ui->tableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
68 
69     connect(ui->tableWidget, &QTableWidget::cellClicked, this, &VWidgetDetails::InLayoutStateChanged);
70     connect(ui->tableWidget, &QTableWidget::customContextMenuRequested, this, &VWidgetDetails::ShowContextMenu);
71 
72     m_updateListTimer->setSingleShot(true);
73     connect(m_updateListTimer, &QTimer::timeout, this, [this]()
74     {
75         FillTable(m_data->DataPieces());
76     });
77 }
78 
79 //---------------------------------------------------------------------------------------------------------------------
~VWidgetDetails()80 VWidgetDetails::~VWidgetDetails()
81 {
82     delete ui;
83 }
84 
85 //---------------------------------------------------------------------------------------------------------------------
UpdateList()86 void VWidgetDetails::UpdateList()
87 {
88     // The filling table is a very expensive operation. This optimization will postpone it.
89     // Each time a new request happen we will wait 800 ms before calling it. If at this time a new request will arrive
90     // we will wait 800 ms more. And so on, until nothing happens within 800ms.
91     m_updateListTimer->start(800);
92 }
93 
94 //---------------------------------------------------------------------------------------------------------------------
SelectDetail(quint32 id)95 void VWidgetDetails::SelectDetail(quint32 id)
96 {
97     const int rowCount = ui->tableWidget->rowCount();
98     for (int row = 0; row < rowCount; ++row)
99     {
100         QTableWidgetItem *item = ui->tableWidget->item(row, PieceColumn::InLayout);
101 
102         if (item->data(Qt::UserRole).toUInt() == id)
103         {
104             ui->tableWidget->setCurrentItem(item);
105             return;
106         }
107     }
108 }
109 
110 //---------------------------------------------------------------------------------------------------------------------
ToggledPiece(quint32 id)111 void VWidgetDetails::ToggledPiece(quint32 id)
112 {
113     const int rowCount = ui->tableWidget->rowCount();
114     for (int row = 0; row < rowCount; ++row)
115     {
116         QTableWidgetItem *item = ui->tableWidget->item(row, PieceColumn::InLayout);
117 
118         if (item && item->data(Qt::UserRole).toUInt() == id)
119         {
120             ToggledPieceItem(item);
121             return;
122         }
123     }
124 }
125 
126 //---------------------------------------------------------------------------------------------------------------------
InLayoutStateChanged(int row,int column)127 void VWidgetDetails::InLayoutStateChanged(int row, int column)
128 {
129     QTableWidgetItem *item = ui->tableWidget->item(row, PieceColumn::InLayout);
130     const quint32 id = item->data(Qt::UserRole).toUInt();
131     emit Highlight(id);
132 
133     if (column != 0)
134     {
135         return;
136     }
137 
138     const QHash<quint32, VPiece> *allDetails = m_data->DataPieces();
139     const bool inLayout = not allDetails->value(id).IsInLayout();
140 
141     TogglePieceInLayout *togglePrint = new TogglePieceInLayout(id, inLayout, m_data, m_doc);
142     connect(togglePrint, &TogglePieceInLayout::Toggled, this, &VWidgetDetails::ToggledPiece);
143     VAbstractApplication::VApp()->getUndoStack()->push(togglePrint);
144 }
145 
146 //---------------------------------------------------------------------------------------------------------------------
FillTable(const QHash<quint32,VPiece> * details)147 void VWidgetDetails::FillTable(const QHash<quint32, VPiece> *details)
148 {
149     const int selectedRow = ui->tableWidget->currentRow();
150     ui->tableWidget->clearContents();
151 
152     ui->tableWidget->setColumnCount(2);
153     ui->tableWidget->setRowCount(details->size());
154     qint32 currentRow = -1;
155     auto i = details->constBegin();
156     while (i != details->constEnd())
157     {
158         ++currentRow;
159         const VPiece det = i.value();
160 
161         ui->tableWidget->setItem(currentRow, PieceColumn::InLayout, PrepareInLayoutColumnCell(det, i.key()));
162         ui->tableWidget->setItem(currentRow, PieceColumn::PieceName, PreparePieceNameColumnCell(det));
163         ++i;
164     }
165     ui->tableWidget->sortItems(PieceColumn::PieceName, Qt::AscendingOrder);
166     ui->tableWidget->resizeColumnsToContents();
167     ui->tableWidget->resizeRowsToContents();
168 
169     ui->tableWidget->setCurrentCell(selectedRow, 0);
170 
171     on_checkBoxHideNotInLayout_stateChanged(); // Trigger hide for action from context menu
172 }
173 
174 //---------------------------------------------------------------------------------------------------------------------
ToggleSectionDetails(bool select)175 void VWidgetDetails::ToggleSectionDetails(bool select)
176 {
177     const QHash<quint32, VPiece> *allDetails = m_data->DataPieces();
178     if (allDetails->count() == 0)
179     {
180         return;
181     }
182 
183     for (int i = 0; i<ui->tableWidget->rowCount(); ++i)
184     {
185         const quint32 id = ui->tableWidget->item(i, PieceColumn::InLayout)->data(Qt::UserRole).toUInt();
186         if (allDetails->contains(id))
187         {
188             if (not (select == allDetails->value(id).IsInLayout()))
189             {
190                 TogglePieceInLayout *togglePrint = new TogglePieceInLayout(id, select, m_data, m_doc);
191                 connect(togglePrint, &TogglePieceInLayout::Toggled, this, &VWidgetDetails::ToggledPiece);
192                 VAbstractApplication::VApp()->getUndoStack()->push(togglePrint);
193             }
194         }
195     }
196 }
197 
198 //---------------------------------------------------------------------------------------------------------------------
ToggledPieceItem(QTableWidgetItem * item)199 void VWidgetDetails::ToggledPieceItem(QTableWidgetItem *item)
200 {
201     SCASSERT(item != nullptr)
202 
203     quint32 id = item->data(Qt::UserRole).toUInt();
204     const QHash<quint32, VPiece> *details = m_data->DataPieces();
205 
206     if (details->contains(id))
207     {
208         const bool inLayout = details->value(id).IsInLayout();
209         inLayout ? item->setIcon(QIcon(*allowDetailIcon))
210                  : item->setIcon(QIcon(*forbidDetailIcon));
211 
212         VToolSeamAllowance *tool = nullptr;
213         try
214         {
215             tool = qobject_cast<VToolSeamAllowance*>(VAbstractPattern::getTool(id));
216             tool->setVisible(ui->checkBoxHideNotInLayout->isChecked() ? inLayout : true);
217         }
218         catch (VExceptionBadId &)
219         {
220             // do nothing
221         }
222     }
223 }
224 
225 //---------------------------------------------------------------------------------------------------------------------
PrepareInLayoutColumnCell(const VPiece & det,quint32 id) const226 QTableWidgetItem *VWidgetDetails::PrepareInLayoutColumnCell(const VPiece &det, quint32 id) const
227 {
228     QTableWidgetItem *item = new QTableWidgetItem();
229     item->setTextAlignment(Qt::AlignHCenter);
230     item->setIcon(det.IsInLayout() ? QIcon(*allowDetailIcon) : QIcon(*forbidDetailIcon));
231     item->setData(Qt::UserRole, id);
232 
233     // set the item non-editable (view only), and non-selectable
234     Qt::ItemFlags flags = item->flags();
235     flags &= ~(Qt::ItemIsEditable); // reset/clear the flag
236     item->setFlags(flags);
237     return item;
238 }
239 
240 //---------------------------------------------------------------------------------------------------------------------
PreparePieceNameColumnCell(const VPiece & det) const241 QTableWidgetItem *VWidgetDetails::PreparePieceNameColumnCell(const VPiece &det) const
242 {
243     QString name = det.GetName();
244     if (name.isEmpty())
245     {
246         name = tr("Unnamed");
247     }
248 
249     QTableWidgetItem *item = new QTableWidgetItem(name);
250     item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
251 
252     // set the item non-editable (view only), and non-selectable
253     Qt::ItemFlags flags = item->flags();
254     flags &= ~(Qt::ItemIsEditable); // reset/clear the flag
255     item->setFlags(flags);
256     return item;
257 }
258 
259 //---------------------------------------------------------------------------------------------------------------------
ShowContextMenu(const QPoint & pos)260 void VWidgetDetails::ShowContextMenu(const QPoint &pos)
261 {
262     QScopedPointer<QMenu> menu(new QMenu());
263     QAction *actionSelectAll = menu->addAction(tr("Select all"));
264     QAction *actionSelectNone = menu->addAction(tr("Select none"));
265 
266     menu->addSeparator();
267 
268     QAction *actionInvertSelection = menu->addAction(tr("Invert selection"));
269 
270     bool pieceMode = false;
271     QAction *actionPieceOptions = nullptr;
272     QAction *actionDeletePiece = nullptr;
273     VToolSeamAllowance *toolPiece = nullptr;
274 
275     QTableWidgetItem *selectedItem = ui->tableWidget->itemAt(pos);
276     if (selectedItem)
277     {
278         QTableWidgetItem *item = ui->tableWidget->item(selectedItem->row(), PieceColumn::InLayout);
279         const quint32 id = item->data(Qt::UserRole).toUInt();
280 
281         try
282         {
283             toolPiece = qobject_cast<VToolSeamAllowance *>(VAbstractPattern::getTool(id));
284             if (toolPiece)
285             {
286                 pieceMode = true;
287                 menu->addSeparator();
288 
289                 actionPieceOptions = menu->addAction(QIcon::fromTheme(QStringLiteral("preferences-other")),
290                                                      tr("Piece options"));
291 
292                 actionDeletePiece = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-delete")),
293                                                      tr("Delete piece"));
294                 actionDeletePiece->setDisabled(toolPiece->referens() > 0);
295             }
296         }
297         catch (const VExceptionBadId &)
298         {
299             const QString errorMsg = tr("Cannot find piece by id '%1'").arg(id);
300             qWarning() << VAbstractValApplication::warningMessageSignature + errorMsg;
301         }
302     }
303 
304     const QHash<quint32, VPiece> *allDetails = m_data->DataPieces();
305     if (allDetails->count() == 0)
306     {
307         return;
308     }
309 
310     int selectedDetails = 0;
311 
312     auto iter = allDetails->constBegin();
313     while (iter != allDetails->constEnd())
314     {
315         if(iter.value().IsInLayout())
316         {
317             selectedDetails++;
318         }
319         ++iter;
320     }
321 
322     if (selectedDetails == 0)
323     {
324         actionSelectNone->setDisabled(true);
325     }
326     else if (selectedDetails == allDetails->size())
327     {
328         actionSelectAll->setDisabled(true);
329     }
330 
331     QAction *selectedAction = menu->exec(ui->tableWidget->viewport()->mapToGlobal(pos));
332 
333     bool select;
334     if (selectedAction == actionSelectAll)
335     {
336         select = true;
337         VAbstractApplication::VApp()->getUndoStack()->beginMacro(tr("select all details"));
338         ToggleSectionDetails(select);
339         VAbstractApplication::VApp()->getUndoStack()->endMacro();
340     }
341     else if (selectedAction == actionSelectNone)
342     {
343         select = false;
344         VAbstractApplication::VApp()->getUndoStack()->beginMacro(tr("select none details"));
345         ToggleSectionDetails(select);
346         VAbstractApplication::VApp()->getUndoStack()->endMacro();
347     }
348     else if (selectedAction == actionInvertSelection)
349     {
350         VAbstractApplication::VApp()->getUndoStack()->beginMacro(tr("invert selection"));
351 
352         for (int i = 0; i<ui->tableWidget->rowCount(); ++i)
353         {
354             QTableWidgetItem *item = ui->tableWidget->item(i, PieceColumn::InLayout);
355             const quint32 id = item->data(Qt::UserRole).toUInt();
356             if (allDetails->contains(id))
357             {
358                 select = not allDetails->value(id).IsInLayout();
359 
360                 TogglePieceInLayout *togglePrint = new TogglePieceInLayout(id, select, m_data, m_doc);
361                 connect(togglePrint, &TogglePieceInLayout::Toggled, this, &VWidgetDetails::ToggledPiece);
362                 VAbstractApplication::VApp()->getUndoStack()->push(togglePrint);
363             }
364         }
365 
366         VAbstractApplication::VApp()->getUndoStack()->endMacro();
367     }
368     else if (pieceMode && selectedAction == actionPieceOptions)
369     {
370         toolPiece->ShowOptions();
371     }
372     else if (pieceMode && selectedAction == actionDeletePiece)
373     {
374         try
375         {
376             toolPiece->DeleteFromMenu();
377         }
378         catch(const VExceptionToolWasDeleted &e)
379         {
380             Q_UNUSED(e);
381             return;//Leave this method immediately!!!
382         }
383         //Leave this method immediately after call!!!
384     }
385 }
386 
387 //------------------------------------------------------------------------------------------------------------------
388 /**
389  * @brief
390  * enable "in layout" details visible or "not in layout" hidden
391  */
on_checkBoxHideNotInLayout_stateChanged()392 void VWidgetDetails::on_checkBoxHideNotInLayout_stateChanged()
393 {
394     for (int i = 0; i < ui->tableWidget->rowCount(); ++i)
395     {
396         if (QTableWidgetItem *item = ui->tableWidget->item(i, PieceColumn::InLayout))
397         {
398             ToggledPieceItem(item);
399         }
400     }
401 }
402