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