Warning, file /office/skrooge/skgbasegui/skgtreeview.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr 0003 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 ***************************************************************************/ 0006 /** @file 0007 * A tree view with more features. 0008 * 0009 * @author Stephane MANKOWSKI / Guillaume DE BURE 0010 */ 0011 #include "skgtreeview.h" 0012 0013 #include <klocalizedstring.h> 0014 #include <kstandardaction.h> 0015 0016 #include <qapplication.h> 0017 #include <qbasictimer.h> 0018 #include <qclipboard.h> 0019 #include <qdesktopservices.h> 0020 #include <qdom.h> 0021 #include <qevent.h> 0022 #include <qfileinfo.h> 0023 #include <qheaderview.h> 0024 #include <qicon.h> 0025 #include <qmenu.h> 0026 #include <qpainter.h> 0027 #include <qprinter.h> 0028 #include <qpushbutton.h> 0029 #include <qsavefile.h> 0030 #include <qscrollbar.h> 0031 #include <qsvggenerator.h> 0032 #include <qtextbrowser.h> 0033 #include <qtextcodec.h> 0034 #include <qtextdocumentwriter.h> 0035 #include <qtextobject.h> 0036 #include <qtexttable.h> 0037 #include <qtimer.h> 0038 0039 #include <algorithm> 0040 0041 #include "skgdocument.h" 0042 #include "skgerror.h" 0043 #include "skgmainpanel.h" 0044 #include "skgobjectbase.h" 0045 #include "skgobjectmodelbase.h" 0046 #include "skgpropertyobject.h" 0047 #include "skgservices.h" 0048 #include "skgsortfilterproxymodel.h" 0049 #include "skgtableview.h" 0050 #include "skgtraces.h" 0051 #include "skgtransactionmng.h" 0052 0053 SKGTreeView::SKGTreeView(QWidget* iParent) 0054 : QTreeView(iParent), 0055 m_autoResize(true), m_autoResizeDone(false), m_actAutoResize(nullptr), 0056 m_document(nullptr), m_textResizable(true), 0057 m_model(nullptr), m_proxyModel(nullptr), 0058 m_actGroupByNone(nullptr), 0059 stickH(false), stickV(false) 0060 { 0061 // Initialize 0062 setTextElideMode(Qt::ElideMiddle); 0063 setAutoExpandDelay(300); 0064 setAnimated(true); 0065 0066 m_timerDelayedResize.setSingleShot(true); 0067 connect(&m_timerDelayedResize, &QTimer::timeout, this, &SKGTreeView::resizeColumnsToContents, Qt::QueuedConnection); 0068 0069 m_timerSelectionChanged.setSingleShot(true); 0070 connect(&m_timerSelectionChanged, &QTimer::timeout, this, &SKGTreeView::selectionChangedDelayed, Qt::QueuedConnection); 0071 0072 m_timerScrollSelection.setSingleShot(true); 0073 connect(&m_timerScrollSelection, &QTimer::timeout, this, &SKGTreeView::scroolOnSelection, Qt::QueuedConnection); 0074 0075 // Menu 0076 QHeaderView* hori = header(); 0077 hori->setContextMenuPolicy(Qt::CustomContextMenu); 0078 m_headerMenu = new QMenu(this); 0079 0080 setContextMenuPolicy(Qt::ActionsContextMenu); 0081 connect(hori, &QHeaderView::customContextMenuRequested, this, static_cast<void (SKGTreeView::*)(const QPoint)>(&SKGTreeView::showHeaderMenu)); 0082 connect(hori, &QHeaderView::sortIndicatorChanged, this, &SKGTreeView::onSortChanged); 0083 0084 // 0085 m_actCopy = KStandardAction::copy(this, SLOT(copy()), nullptr); 0086 m_actCopy->setProperty("isShortcutConfigurable", false); 0087 m_actCopy->setShortcutContext(Qt::WidgetShortcut); 0088 0089 m_actExpandAll = new QAction(SKGServices::fromTheme(QStringLiteral("format-indent-more")), i18nc("Noun, user action", "Expand all"), this); 0090 m_actExpandAll->setShortcut(Qt::ALT + Qt::Key_Plus); 0091 m_actExpandAll->setProperty("isShortcutConfigurable", false); 0092 m_actExpandAll->setShortcutContext(Qt::WidgetShortcut); 0093 connect(m_actExpandAll, &QAction::triggered, this, &SKGTreeView::expandAll); 0094 0095 m_actCollapseAll = new QAction(SKGServices::fromTheme(QStringLiteral("format-indent-less")), i18nc("Noun, user action", "Collapse all"), this); 0096 m_actCollapseAll->setShortcut(Qt::ALT + Qt::Key_Minus); 0097 m_actCollapseAll->setProperty("isShortcutConfigurable", false); 0098 m_actCollapseAll->setShortcutContext(Qt::WidgetShortcut); 0099 connect(m_actCollapseAll, &QAction::triggered, this, &SKGTreeView::collapseAll); 0100 if (SKGMainPanel::getMainPanel() != nullptr) { 0101 SKGMainPanel::getMainPanel()->registerGlobalAction(QStringLiteral("edit_copy"), m_actCopy); 0102 SKGMainPanel::getMainPanel()->registerGlobalAction(QStringLiteral("edit_expandall"), m_actExpandAll); 0103 SKGMainPanel::getMainPanel()->registerGlobalAction(QStringLiteral("edit_collapseall"), m_actCollapseAll); 0104 } 0105 0106 // Scroll bars 0107 connect(horizontalScrollBar(), &QScrollBar::actionTriggered, this, &SKGTreeView::onActionTriggered); 0108 connect(verticalScrollBar(), &QScrollBar::actionTriggered, this, &SKGTreeView::onActionTriggered); 0109 connect(horizontalScrollBar(), &QScrollBar::rangeChanged, this, &SKGTreeView::onRangeChanged); 0110 connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &SKGTreeView::onRangeChanged); 0111 0112 // Headers 0113 hori->setSectionsMovable(true); 0114 hori->setSectionResizeMode(QHeaderView::Fixed); 0115 setWordWrap(false); 0116 0117 connect(header(), &QHeaderView::sectionMoved, this, &SKGTreeView::setupHeaderMenu, Qt::QueuedConnection); 0118 0119 connect(this, &SKGTreeView::clicked, this, &SKGTreeView::onClick); 0120 connect(this, &SKGTreeView::collapsed, this, &SKGTreeView::onCollapse); 0121 connect(this, &SKGTreeView::expanded, this, &SKGTreeView::onExpand); 0122 0123 QWidget* vp = this->viewport(); 0124 if (vp != nullptr) { 0125 vp->installEventFilter(this); 0126 this->installEventFilter(this); 0127 } 0128 0129 // Save original size 0130 m_fontOriginalPointSize = this->font().pointSize(); 0131 m_iconOriginalSize = this->iconSize().height(); 0132 if (m_iconOriginalSize <= 0) { 0133 m_iconOriginalSize = 16; 0134 } 0135 } 0136 0137 SKGTreeView::~SKGTreeView() 0138 { 0139 m_document = nullptr; 0140 m_headerMenu = nullptr; 0141 m_proxyModel = nullptr; 0142 m_model = nullptr; 0143 m_actExpandAll = nullptr; 0144 m_actCollapseAll = nullptr; 0145 } 0146 0147 bool SKGTreeView::isAutoResized() 0148 { 0149 return m_autoResize; 0150 } 0151 0152 QString SKGTreeView::getState() 0153 { 0154 SKGTRACEINFUNC(10) 0155 QDomDocument doc(QStringLiteral("SKGML")); 0156 QDomElement root = doc.createElement(QStringLiteral("parameters")); 0157 doc.appendChild(root); 0158 0159 QHeaderView* hHeader = header(); 0160 if ((hHeader != nullptr) && (m_model != nullptr)) { 0161 if (isSortingEnabled()) { 0162 root.setAttribute(QStringLiteral("sortOrder"), SKGServices::intToString(static_cast<int>(hHeader->sortIndicatorOrder()))); 0163 root.setAttribute(QStringLiteral("sortColumn"), m_model->getAttribute(hHeader->sortIndicatorSection())); 0164 0165 if (m_proxyModel != nullptr) { 0166 root.setAttribute(QStringLiteral("sortPreviousColumn"), SKGServices::intToString(m_proxyModel->getPreviousSortColumn())); 0167 } 0168 } 0169 root.setAttribute(QStringLiteral("groupBy"), m_groupby); 0170 0171 // Memorize order 0172 int nb = hHeader->count(); 0173 if (nb != 0) { 0174 QString columns; 0175 QString columnsSize; 0176 QString columnsVisibility; 0177 for (int i = 0; i < nb; ++i) { 0178 int idx = hHeader->logicalIndex(i); 0179 if (i != 0) { 0180 columns += ';'; 0181 } 0182 columns += m_model->getAttribute(idx); 0183 0184 if (i != 0) { 0185 columnsSize += ';'; 0186 } 0187 columnsSize += SKGServices::intToString(hHeader->sectionSize(idx)); 0188 0189 if (i != 0) { 0190 columnsVisibility += ';'; 0191 } 0192 columnsVisibility += (hHeader->isSectionHidden(idx) ? QStringLiteral("N") : QStringLiteral("Y")); 0193 } 0194 root.setAttribute(QStringLiteral("columns"), columns); 0195 if (!m_autoResize) { 0196 root.setAttribute(QStringLiteral("columnsSize"), columnsSize); 0197 } 0198 root.setAttribute(QStringLiteral("columnsVisibility"), columnsVisibility); 0199 root.setAttribute(QStringLiteral("columnsAutoResize"), m_autoResize ? QStringLiteral("Y") : QStringLiteral("N")); 0200 } 0201 0202 // Memorize expanded groups 0203 if (!m_groupby.isEmpty()) { 0204 root.setAttribute(QStringLiteral("expandedGroups"), m_expandedNodes.join(QStringLiteral(";"))); 0205 } 0206 } 0207 root.setAttribute(QStringLiteral("alternatingRowColors"), alternatingRowColors() ? QStringLiteral("Y") : QStringLiteral("N")); 0208 root.setAttribute(QStringLiteral("zoomPosition"), SKGServices::intToString(zoomPosition())); 0209 0210 QScrollBar* scroll2 = horizontalScrollBar(); 0211 if ((scroll2 != nullptr) && scroll2->value() == scroll2->maximum() && scroll2->value() != scroll2->minimum()) { 0212 root.setAttribute(QStringLiteral("stickH"), QStringLiteral("Y")); 0213 } 0214 scroll2 = verticalScrollBar(); 0215 if ((scroll2 != nullptr) && scroll2->value() == scroll2->maximum() && scroll2->value() != scroll2->minimum()) { 0216 root.setAttribute(QStringLiteral("stickV"), QStringLiteral("Y")); 0217 } 0218 return doc.toString(-1); 0219 } 0220 0221 void SKGTreeView::setState(const QString& iState) 0222 { 0223 SKGTRACEINFUNC(10) 0224 resetColumnsOrder(); 0225 0226 QDomDocument doc(QStringLiteral("SKGML")); 0227 0228 QString viewState = iState; 0229 if (viewState.isEmpty() && (m_document != nullptr)) { 0230 // Get default state 0231 viewState = m_document->getParameter(m_parameterName); 0232 } 0233 0234 Qt::SortOrder qtsortorder = Qt::AscendingOrder; 0235 if (doc.setContent(viewState)) { 0236 QDomElement root = doc.documentElement(); 0237 0238 QString sortOrder = root.attribute(QStringLiteral("sortOrder")); 0239 QString sortColumn = root.attribute(QStringLiteral("sortColumn")); 0240 QString sortPreviousColumn = root.attribute(QStringLiteral("sortPreviousColumn")); 0241 m_groupby = root.attribute(QStringLiteral("groupBy")); 0242 QString columns = root.attribute(QStringLiteral("columns")); 0243 QString columnsSize = root.attribute(QStringLiteral("columnsSize")); 0244 QString columnsVisibility = root.attribute(QStringLiteral("columnsVisibility")); 0245 QString columnsAutoResize = root.attribute(QStringLiteral("columnsAutoResize")); 0246 QString alternatingRowColors2 = root.attribute(QStringLiteral("alternatingRowColors")); 0247 QString zoomPositionString = root.attribute(QStringLiteral("zoomPosition")); 0248 QString expandedGroups = root.attribute(QStringLiteral("expandedGroups")); 0249 stickH = (root.attribute(QStringLiteral("stickH")) == QStringLiteral("Y")); 0250 stickV = (root.attribute(QStringLiteral("stickV")) == QStringLiteral("Y")); 0251 0252 // Set column order 0253 QStringList listAtt; 0254 if (!columns.isEmpty()) { 0255 listAtt = SKGServices::splitCSVLine(columns, ';'); 0256 QStringList sizes = SKGServices::splitCSVLine(columnsSize, ';'); 0257 QStringList visibilities = SKGServices::splitCSVLine(columnsVisibility, ';'); 0258 0259 int nb = listAtt.count(); 0260 int nbvisibilities = visibilities.count(); 0261 int nbsizes = sizes.count(); 0262 for (int i = 0; i < nb; ++i) { 0263 if (nbvisibilities == nb) { 0264 listAtt[i] = listAtt.at(i) % '|' % visibilities.at(i); 0265 if (nbsizes == nb) { 0266 listAtt[i] = listAtt.at(i) % '|' % sizes.at(i); 0267 } 0268 } 0269 } 0270 } 0271 if (m_model != nullptr) { 0272 m_model->setSupportedAttributes(listAtt); 0273 } 0274 0275 // Set autoResize 0276 if (!columnsAutoResize.isEmpty()) { 0277 m_autoResize = (columnsAutoResize == QStringLiteral("Y")); 0278 header()->setSectionResizeMode(m_autoResize ? QHeaderView::Fixed : QHeaderView::Interactive); 0279 if (!m_autoResize) { 0280 m_timerDelayedResize.stop(); 0281 m_autoResizeDone = false; 0282 } 0283 } 0284 0285 // Set sort 0286 if ((m_proxyModel != nullptr) && !sortPreviousColumn.isEmpty()) { 0287 m_proxyModel->setPreviousSortColumn(SKGServices::stringToInt(sortPreviousColumn)); 0288 } 0289 if ((m_model != nullptr) && isSortingEnabled() && !sortOrder.isEmpty() && !sortColumn.isEmpty()) { 0290 int index = SKGServices::splitCSVLine(columns, ';').indexOf(sortColumn); 0291 if (index == -1) { 0292 index = m_model->getIndexAttribute(sortColumn); 0293 } 0294 if (index == -1) { 0295 index = 0; 0296 } 0297 qtsortorder = static_cast<Qt::SortOrder>(SKGServices::stringToInt(sortOrder)); 0298 this->sortByColumn(index, qtsortorder); 0299 } 0300 0301 if (m_model != nullptr) { 0302 QString att = m_groupby; 0303 if (att == QStringLiteral("#")) { 0304 att = sortColumn; 0305 } 0306 m_model->setGroupBy(att); 0307 m_model->dataModified(); 0308 0309 refreshExpandCollapse(); 0310 } 0311 0312 // Set alternatingRowColors 0313 if (!alternatingRowColors2.isEmpty()) { 0314 setAlternatingRowColors(alternatingRowColors2 == QStringLiteral("Y")); 0315 } 0316 if (!zoomPositionString.isEmpty()) { 0317 setZoomPosition(SKGServices::stringToInt(zoomPositionString)); 0318 } 0319 0320 // Set expanded groups 0321 m_expandedNodes = SKGServices::splitCSVLine(expandedGroups); 0322 resetSelection(); 0323 } else { 0324 if (m_model != nullptr) { 0325 m_model->setSupportedAttributes(QStringList()); 0326 m_groupby = QLatin1String(""); 0327 m_model->setGroupBy(m_groupby); 0328 m_model->dataModified(); 0329 0330 refreshExpandCollapse(); 0331 } 0332 0333 this->sortByColumn(0, qtsortorder); 0334 } 0335 } 0336 0337 void SKGTreeView::refreshExpandCollapse() 0338 { 0339 bool treeMode = !m_model->getParentChildAttribute().isEmpty(); 0340 setRootIsDecorated(treeMode && m_groupby.isEmpty()); 0341 if (m_actExpandAll != nullptr) { 0342 m_actExpandAll->setVisible(treeMode || !m_groupby.isEmpty()); 0343 } 0344 if (m_actCollapseAll != nullptr) { 0345 m_actCollapseAll->setVisible(treeMode || !m_groupby.isEmpty()); 0346 } 0347 } 0348 0349 void SKGTreeView::respanFirstColumns() 0350 { 0351 // Span groups 0352 int nbRow = m_model->rowCount(); 0353 for (int row = 0; row < nbRow; ++row) { 0354 this -> setFirstColumnSpanned(row, QModelIndex(), !m_groupby.isEmpty()); 0355 } 0356 0357 if (m_autoResize) { 0358 resizeColumnsToContentsDelayed(); 0359 } 0360 } 0361 0362 void SKGTreeView::onRangeChanged() 0363 { 0364 auto* scroll2 = qobject_cast<QScrollBar*>(sender()); 0365 if ((stickH && scroll2 == horizontalScrollBar()) || (stickV && scroll2 == verticalScrollBar())) { 0366 scroll2->setValue(scroll2->maximum()); 0367 } 0368 } 0369 0370 void SKGTreeView::onActionTriggered(int action) 0371 { 0372 auto* scroll2 = qobject_cast<QScrollBar*>(sender()); 0373 if ((scroll2 != nullptr) && action == QAbstractSlider::SliderToMaximum) { 0374 if (scroll2 == horizontalScrollBar()) { 0375 stickH = true; 0376 } 0377 if (scroll2 == verticalScrollBar()) { 0378 stickV = true; 0379 } 0380 } else { 0381 if (scroll2 == horizontalScrollBar()) { 0382 stickH = false; 0383 } 0384 if (scroll2 == verticalScrollBar()) { 0385 stickV = false; 0386 } 0387 } 0388 } 0389 0390 void SKGTreeView::insertGlobalAction(const QString& iRegisteredAction) 0391 { 0392 if (iRegisteredAction.isEmpty()) { 0393 auto sep = new QAction(this); 0394 sep->setSeparator(true); 0395 this->insertAction(nullptr, sep); 0396 } else if (SKGMainPanel::getMainPanel() != nullptr) { 0397 QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(iRegisteredAction); 0398 this->insertAction(nullptr, act); 0399 } 0400 } 0401 0402 void SKGTreeView::resizeColumnsToContentsDelayed() 0403 { 0404 SKGTRACEINFUNC(10) 0405 m_timerDelayedResize.start(300); 0406 } 0407 0408 void SKGTreeView::resizeColumnsToContents() 0409 { 0410 SKGTRACEINFUNC(10) { 0411 SKGTRACEIN(10, "SKGTreeView::resizeColumnsToContents-respanFirstColumns") 0412 auto save = m_autoResize; 0413 m_autoResize = false; // To avoid loop 0414 respanFirstColumns(); 0415 m_autoResize = save; 0416 } 0417 0418 int nb = header()->count(); 0419 for (int i = nb - 1; i >= 0; --i) { 0420 SKGTRACEIN(10, "SKGTreeView::resizeColumnsToContents-resizeColumnToContents(" + SKGServices::intToString(i) + ')') 0421 if (!isColumnHidden(i)) { 0422 resizeColumnToContents(i); 0423 } 0424 } 0425 } 0426 0427 void SKGTreeView::showHeaderMenu() 0428 { 0429 showHeaderMenu(header()->mapFromGlobal(QCursor::pos())); 0430 } 0431 0432 void SKGTreeView::showHeaderMenu(const QPoint iPos) 0433 { 0434 if (m_headerMenu != nullptr) { 0435 m_headerMenu->popup(header()->mapToGlobal(iPos)); 0436 } 0437 } 0438 0439 void SKGTreeView::setDefaultSaveParameters(SKGDocument* iDocument, const QString& iParameterName) 0440 { 0441 m_document = iDocument; 0442 m_parameterName = iParameterName; 0443 } 0444 0445 void SKGTreeView::setupHeaderMenu() 0446 { 0447 SKGTRACEINFUNC(10) 0448 if ((m_model != nullptr) && m_model->isRefreshBlocked()) { 0449 return; 0450 } 0451 0452 setCornerWidget(nullptr); 0453 if ((m_model != nullptr) && (m_headerMenu != nullptr)) { 0454 // Corner button on tables to show the contextual menu 0455 auto btn = new QPushButton(this); 0456 btn->setIcon(SKGServices::fromTheme(QStringLiteral("configure"))); 0457 btn->setFocusPolicy(Qt::NoFocus); 0458 connect(btn, &QPushButton::clicked, this, static_cast<void (SKGTreeView::*)()>(&SKGTreeView::showHeaderMenu)); 0459 setCornerWidget(btn); 0460 0461 m_headerMenu->clear(); 0462 0463 // Processing 0464 QMenu* columns = m_headerMenu->addMenu(i18nc("Noun, Menu name", "Columns")); 0465 0466 // Get current groupby column 0467 QMenu* groupbymenu = nullptr; 0468 QActionGroup* groupby = nullptr; 0469 if (!m_model->getWhereClause().contains(QStringLiteral("ORDER BY"))) { 0470 groupbymenu = m_headerMenu->addMenu(i18nc("Noun, Menu name", "Group by")); 0471 groupby = new QActionGroup(groupbymenu); 0472 m_actGroupByNone = groupbymenu->addAction(i18nc("Noun, grouping option", "None")); 0473 if (m_actGroupByNone != nullptr) { 0474 m_actGroupByNone->setIcon(SKGServices::fromTheme(QStringLiteral("dialog-cancel"))); 0475 m_actGroupByNone->setCheckable(true); 0476 m_actGroupByNone->setChecked(m_groupby.isEmpty()); 0477 m_actGroupByNone->setData(QLatin1String("")); 0478 groupby->addAction(m_actGroupByNone); 0479 } 0480 if (m_proxyModel != nullptr) { 0481 QAction* actSort = groupbymenu->addAction(i18nc("Noun, grouping option", "Sorted column")); 0482 if (actSort != nullptr) { 0483 actSort->setIcon(SKGServices::fromTheme(QStringLiteral("view-sort-ascending"))); 0484 actSort->setCheckable(true); 0485 actSort->setChecked(m_groupby == QStringLiteral("#")); 0486 actSort->setData(QStringLiteral("#")); 0487 groupby->addAction(actSort); 0488 } 0489 } 0490 groupbymenu->addSeparator(); 0491 } 0492 0493 // Set right click menu 0494 SKGDocument::SKGModelTemplateList schemas = m_model->getSchemas(); 0495 int nbSchemas = schemas.count(); 0496 if (nbSchemas != 0) { 0497 QMenu* viewAppearanceMenu = columns->addMenu(SKGServices::fromTheme(QStringLiteral("view-file-columns")), i18nc("Noun, user action", "View appearance")); 0498 0499 for (int i = 0; i < nbSchemas; ++i) { 0500 SKGDocument::SKGModelTemplate schema = schemas.at(i); 0501 QAction* act = viewAppearanceMenu->addAction(schema.name); 0502 if (!schema.icon.isEmpty()) { 0503 act->setIcon(SKGServices::fromTheme(schema.icon)); 0504 } 0505 act->setData(schema.schema); 0506 0507 connect(act, &QAction::triggered, this, &SKGTreeView::changeSchema); 0508 } 0509 } 0510 0511 QAction* actResize = columns->addAction(SKGServices::fromTheme(QStringLiteral("zoom-fit-width")), i18nc("Noun, user action", "Resize to content")); 0512 connect(actResize, &QAction::triggered, this, &SKGTreeView::resizeColumnsToContents); 0513 0514 m_actAutoResize = columns->addAction(SKGServices::fromTheme(QStringLiteral("zoom-fit-width"), QStringList() << QStringLiteral("run-build")), i18nc("Noun, user action", "Auto resize")); 0515 m_actAutoResize->setCheckable(true); 0516 m_actAutoResize->setChecked(m_autoResize); 0517 connect(m_actAutoResize, &QAction::triggered, this, &SKGTreeView::switchAutoResize); 0518 0519 QAction* actAlternatingRowColors = m_headerMenu->addAction(i18nc("Noun, user action", "Alternate row colors")); 0520 if (actAlternatingRowColors != nullptr) { 0521 actAlternatingRowColors->setCheckable(true); 0522 actAlternatingRowColors->setChecked(alternatingRowColors()); 0523 connect(actAlternatingRowColors, &QAction::triggered, this, &SKGTreeView::setAlternatingRowColors); 0524 } 0525 0526 if (m_document != nullptr) { 0527 QAction* actDefault = m_headerMenu->addAction(SKGServices::fromTheme(QStringLiteral("document-save")), i18nc("Noun, user action", "Save parameters")); 0528 connect(actDefault, &QAction::triggered, this, &SKGTreeView::saveDefaultClicked); 0529 } 0530 0531 columns->addSeparator(); 0532 0533 // Build menus for columns 0534 QHeaderView* hHeader = header(); 0535 int nbcol = hHeader->count(); 0536 for (int i = 0; i < nbcol; ++i) { 0537 int idx = hHeader->logicalIndex(i); 0538 QString col = m_model->headerData(idx, Qt::Horizontal, Qt::UserRole).toString(); 0539 QStringList values = col.split('|'); 0540 0541 if (!m_autoResizeDone) { 0542 if (values.count() > 1) { 0543 hHeader->setSectionHidden(idx, values.at(1) == QStringLiteral("N")); 0544 } 0545 if (values.count() > 2) { 0546 auto s = SKGServices::stringToInt(values.at(2)); 0547 if (s > 0) { 0548 hHeader->resizeSection(idx, s); 0549 } 0550 } 0551 } 0552 0553 // Column menu 0554 QAction* act = columns->addAction(values.at(0)); 0555 if (act != nullptr) { 0556 act->setCheckable(true); 0557 act->setChecked(!hHeader->isSectionHidden(idx)); 0558 act->setIcon(m_model->headerData(idx, Qt::Horizontal, Qt::DecorationRole).value<QIcon>()); 0559 act->setData(idx); 0560 act->setEnabled(i > 0); 0561 0562 connect(act, &QAction::triggered, this, &SKGTreeView::showHideColumn); 0563 0564 // Group by menu 0565 QString att = m_model->getAttribute(idx); 0566 if ((groupbymenu != nullptr) && (groupby != nullptr)) { 0567 QAction* act2 = groupbymenu->addAction(values.at(0)); 0568 if (act2 != nullptr) { 0569 act2->setCheckable(true); 0570 act2->setChecked(att == m_groupby); 0571 act2->setIcon(act->icon()); 0572 act2->setData(att); 0573 groupby->addAction(act2); 0574 } 0575 } 0576 } 0577 } 0578 m_autoResizeDone = true; 0579 if (groupby != nullptr) { 0580 connect(groupby, &QActionGroup::triggered, this, &SKGTreeView::groupByChanged); 0581 } 0582 m_headerMenu->addSeparator(); 0583 0584 QAction* actExport = m_headerMenu->addAction(SKGServices::fromTheme(QStringLiteral("document-export")), i18nc("Noun, user action", "Export…")); 0585 connect(actExport, &QAction::triggered, this, &SKGTreeView::onExport); 0586 0587 if (m_autoResize) { 0588 resizeColumnsToContentsDelayed(); 0589 } 0590 } 0591 } 0592 0593 void SKGTreeView::setSelectionModel(QItemSelectionModel* iSelectionModel) 0594 { 0595 if (this->selectionModel() != nullptr) { 0596 disconnect(this->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SKGTreeView::onSelectionChanged); 0597 } 0598 QTreeView::setSelectionModel(iSelectionModel); 0599 if (iSelectionModel != nullptr) { 0600 connect(iSelectionModel, &QItemSelectionModel::selectionChanged, this, &SKGTreeView::onSelectionChanged); 0601 } 0602 } 0603 0604 void SKGTreeView::onSelectionChanged() 0605 { 0606 SKGTRACEINFUNC(10) 0607 SKGObjectBase::SKGListSKGObjectBase selection; 0608 SKGObjectBase::SKGListSKGObjectBase selectionparents; 0609 QItemSelectionModel* selModel = selectionModel(); 0610 if (selModel != nullptr) { 0611 if (m_model != nullptr) { 0612 auto indexes = selModel->selectedRows(); 0613 0614 int nb = indexes.count(); 0615 QVector<QModelIndex> indexesToSource; 0616 indexesToSource.reserve(indexes.count()); 0617 for (const auto& index : qAsConst(indexes)) { 0618 indexesToSource.push_back(m_proxyModel != nullptr ? m_proxyModel->mapToSource(index) : index); 0619 } 0620 0621 selection.reserve(indexesToSource.count()); 0622 selectionparents.reserve(nb); 0623 for (int i = 0; i < nb; ++i) { 0624 QModelIndex index = indexes.at(i); 0625 QModelIndex idxs = indexesToSource.at(i); 0626 0627 // Get above 0628 QModelIndex idxtmp = (m_proxyModel != nullptr ? m_proxyModel->mapToSource(indexAbove(index)) : indexAbove(index)); 0629 auto* tmp = m_model->getObjectPointer(idxtmp); 0630 if (tmp != nullptr && idxtmp.parent() == idxs.parent() && !indexesToSource.contains(idxtmp)) { 0631 selectionparents.push_back(*tmp); 0632 } else { 0633 // Get below 0634 idxtmp = (m_proxyModel != nullptr ? m_proxyModel->mapToSource(indexBelow(index)) : indexBelow(index)); 0635 tmp = m_model->getObjectPointer(idxtmp); 0636 if (tmp != nullptr && idxtmp.parent() == idxs.parent() && !indexesToSource.contains(idxtmp)) { 0637 selectionparents.push_back(*tmp); 0638 } else { 0639 // Get parent 0640 tmp = m_model->getObjectPointer(idxs.parent()); 0641 if (tmp != nullptr) { 0642 selectionparents.push_back(*tmp); 0643 } 0644 } 0645 } 0646 0647 SKGObjectBase obj = m_model->getObject(idxs); 0648 selection.push_back(obj); 0649 } 0650 } 0651 } 0652 0653 // if (selection != m_lastSelection) { // WARNING: selection can be equal but attributes of selected objects modified 0654 m_lastSelection = selection; 0655 m_lastSelection_if_deleted = selectionparents; 0656 m_timerSelectionChanged.start(300); 0657 // } 0658 } 0659 0660 void SKGTreeView::saveDefaultClicked() 0661 { 0662 if (m_document != nullptr) { 0663 SKGError err; 0664 SKGBEGINTRANSACTION(*m_document, i18nc("Noun, name of the user action", "Save default parameters"), err) 0665 err = m_document->setParameter(m_parameterName, getState()); 0666 } 0667 } 0668 0669 void SKGTreeView::switchAutoResize() 0670 { 0671 m_autoResize = m_actAutoResize->isChecked(); 0672 header()->setSectionResizeMode(m_autoResize ? QHeaderView::Fixed : QHeaderView::Interactive); 0673 if (m_autoResize) { 0674 resizeColumnsToContentsDelayed(); 0675 } else { 0676 m_timerDelayedResize.stop(); 0677 m_autoResizeDone = false; 0678 } 0679 } 0680 0681 void SKGTreeView::groupByChanged(QAction* iAction) 0682 { 0683 if ((m_model != nullptr) && m_model->isRefreshBlocked()) { 0684 return; 0685 } 0686 0687 if ((iAction != nullptr) && (m_model != nullptr)) { 0688 m_groupby = iAction->data().toString(); 0689 QString att = m_groupby; 0690 if (att == QStringLiteral("#") && (m_proxyModel != nullptr)) { 0691 att = m_model->getAttribute(m_proxyModel->sortColumn()); 0692 } 0693 m_model->setGroupBy(att); 0694 m_model->refresh(); 0695 0696 refreshExpandCollapse(); 0697 respanFirstColumns(); 0698 } 0699 } 0700 0701 void SKGTreeView::onSortChanged(int iIndex, Qt::SortOrder iOrder) 0702 { 0703 Q_UNUSED(iOrder) 0704 if (m_groupby == QStringLiteral("#") && (m_model != nullptr)) { 0705 m_model->setGroupBy(m_model->getAttribute(iIndex)); 0706 m_model->refresh(); 0707 } 0708 0709 m_timerScrollSelection.start(300); 0710 } 0711 0712 void SKGTreeView::showHideColumn() 0713 { 0714 auto* send = qobject_cast<QAction*>(this->sender()); 0715 if (send != nullptr) { 0716 QHeaderView* hHeader = header(); 0717 0718 int idx = send->data().toInt(); 0719 bool hidden = !hHeader->isSectionHidden(idx); 0720 hHeader->setSectionHidden(idx, hidden); 0721 } 0722 } 0723 0724 void SKGTreeView::resetColumnsOrder() 0725 { 0726 QHeaderView* hHeader = header(); 0727 int nbcol = hHeader->count(); 0728 for (int i = 0; i < nbcol; ++i) { 0729 int idx = hHeader->visualIndex(i); 0730 if (idx != i) { 0731 hHeader->moveSection(idx, i); 0732 } 0733 } 0734 } 0735 0736 void SKGTreeView::changeSchema() 0737 { 0738 QStringList list; 0739 0740 auto* send = qobject_cast<QAction*>(this->sender()); 0741 if (send != nullptr) { 0742 list = SKGServices::splitCSVLine(send->data().toString(), ';'); 0743 } 0744 0745 if (m_model != nullptr) { 0746 // Reset column oder 0747 resetColumnsOrder(); 0748 0749 m_model->setSupportedAttributes(list); 0750 bool tmp = m_autoResizeDone; 0751 m_autoResizeDone = false; 0752 m_model->dataModified(); 0753 m_autoResizeDone = tmp; 0754 header()->setSortIndicator(0, Qt::AscendingOrder); 0755 } 0756 } 0757 0758 QStringList SKGTreeView::getCurrentSchema() const 0759 { 0760 QStringList list; 0761 QHeaderView* hHeader = header(); 0762 if ((hHeader != nullptr) && (m_model != nullptr)) { 0763 int nb = hHeader->count(); 0764 if (nb != 0) { 0765 QString att; 0766 for (int i = 0; i < nb; ++i) { 0767 int idx = hHeader->logicalIndex(i); 0768 att = m_model->getAttribute(idx); 0769 att += QStringLiteral("|") % (hHeader->isSectionHidden(idx) ? QStringLiteral("N") : QStringLiteral("Y")); 0770 att += QStringLiteral("|") % SKGServices::intToString(hHeader->sectionSize(idx)); 0771 0772 list.push_back(att); 0773 } 0774 } 0775 } 0776 return list; 0777 } 0778 void SKGTreeView::setAlternatingRowColors(bool enable) 0779 { 0780 QTreeView::setAlternatingRowColors(enable); 0781 } 0782 0783 SKGObjectBase SKGTreeView::getFirstSelectedObject() 0784 { 0785 return m_lastSelection.value(0); 0786 } 0787 0788 SKGObjectBase::SKGListSKGObjectBase SKGTreeView::getSelectedObjects() 0789 { 0790 return m_lastSelection; 0791 } 0792 0793 int SKGTreeView::getNbSelectedObjects() 0794 { 0795 return m_lastSelection.count(); 0796 } 0797 0798 void SKGTreeView::saveSelection() 0799 { 0800 SKGTRACEINFUNC(10) 0801 0802 m_selection.clear(); 0803 0804 SKGObjectBase::SKGListSKGObjectBase objs = getSelectedObjects(); 0805 int nb = objs.count(); 0806 // We save the selection only if not too big 0807 if (nb <= 100) { 0808 for (int i = 0; i < nb; ++i) { 0809 QString id = objs.at(i).getUniqueID(); 0810 m_selection.push_back(id); 0811 } 0812 } 0813 SKGTRACEL(10) << m_selection.count() << " objects saved" << SKGENDL; 0814 } 0815 0816 void SKGTreeView::selectObject(const QString& iUniqueID) 0817 { 0818 SKGTRACEINFUNC(10) 0819 QStringList tmp; 0820 tmp.push_back(iUniqueID); 0821 selectObjects(tmp, true); 0822 } 0823 0824 void SKGTreeView::selectObjects(const QStringList& iUniqueIDs, bool iFocusOnFirstOne) 0825 { 0826 SKGTRACEINFUNC(10) 0827 SKGTRACEL(10) << iUniqueIDs.count() << " objects to select" << SKGENDL; 0828 int nbset = 0; 0829 QItemSelectionModel* selModel = selectionModel(); 0830 if (selModel != nullptr) { 0831 bool previous = selModel->blockSignals(true); 0832 0833 selModel->clearSelection(); 0834 0835 if (m_model != nullptr) { 0836 // Get all indexes 0837 QVector<QModelIndex> items; 0838 items.reserve(items.count() * 2); 0839 items.push_back(QModelIndex()); 0840 for (int i = 0; i < items.count(); ++i) { // Dynamic size because the list is modified 0841 QModelIndex mi = items.at(i); 0842 int nbRows = m_model->rowCount(mi); 0843 for (int j = 0; j < nbRows; ++j) { 0844 items.push_back(m_model->index(j, 0, mi)); 0845 } 0846 } 0847 items.removeAt(0); 0848 0849 int nbRows = items.count(); 0850 if (nbRows != 0) { 0851 // Expand nodes 0852 bool previousForThis = this->blockSignals(true); 0853 for (int i = 0; i < nbRows; ++i) { 0854 QModelIndex index = items.at(i); 0855 SKGObjectBase obj = m_model->getObject(index); 0856 QString id = obj.getUniqueID(); 0857 if (obj.getTable().isEmpty()) { 0858 // This is a group 0859 id = obj.getAttribute(QStringLiteral("t_title")); 0860 } 0861 if (m_expandedNodes.contains(id)) { 0862 QModelIndex idxs = (m_proxyModel != nullptr ? m_proxyModel->mapFromSource(index) : index); 0863 setExpanded(idxs, true); 0864 } 0865 } 0866 this->blockSignals(previousForThis); 0867 0868 // Set selection 0869 bool focusDone = false; 0870 for (int i = 0; i < nbRows; ++i) { 0871 QModelIndex index = items.at(i); 0872 SKGObjectBase obj = m_model->getObject(index); 0873 if (iUniqueIDs.contains(obj.getUniqueID())) { 0874 QModelIndex idxs = (m_proxyModel != nullptr ? m_proxyModel->mapFromSource(index) : index); 0875 selModel->select(idxs, QItemSelectionModel::Select | QItemSelectionModel::Rows); 0876 selModel->setCurrentIndex(idxs, QItemSelectionModel::NoUpdate); 0877 ++nbset; 0878 if (iFocusOnFirstOne && !focusDone) { 0879 scrollTo(idxs); 0880 focusDone = true; 0881 } 0882 } 0883 } 0884 } 0885 } 0886 selModel->blockSignals(previous); 0887 } 0888 0889 SKGTRACEL(10) << nbset << " objects selected" << SKGENDL; 0890 0891 onSelectionChanged(); 0892 } 0893 0894 void SKGTreeView::resetSelection() 0895 { 0896 SKGTRACEINFUNC(10) 0897 0898 auto lastSelection_parents_save = m_lastSelection_if_deleted; // because selectObjects will modify m_lastSelection_if_deleted 0899 selectObjects(m_selection); 0900 if ((lastSelection_parents_save.count() != 0) && (getSelectedObjects().count() == 0)) { 0901 // Try to select parent objects 0902 int nb = lastSelection_parents_save.count(); 0903 // We save the selection only if not too big 0904 if (nb <= 100) { 0905 QStringList sel; 0906 sel.reserve(nb); 0907 for (int i = 0; i < nb; ++i) { 0908 QString id = lastSelection_parents_save.at(i).getUniqueID(); 0909 sel.push_back(id); 0910 } 0911 0912 selectObjects(sel); 0913 } 0914 } 0915 } 0916 0917 void SKGTreeView::scroolOnSelection() 0918 { 0919 QItemSelectionModel* selModel = selectionModel(); 0920 if (selModel != nullptr) { 0921 if (m_model != nullptr) { 0922 QModelIndexList indexes = selModel->selectedRows(); 0923 if (!indexes.isEmpty()) { 0924 scrollTo(indexes.at(0)); 0925 } 0926 } 0927 } 0928 } 0929 0930 void SKGTreeView::onExpand(const QModelIndex& index) 0931 { 0932 SKGTRACEINFUNC(10) 0933 if (index.isValid() && (m_model != nullptr)) { 0934 QModelIndex idxs = (m_proxyModel != nullptr ? m_proxyModel->mapToSource(index) : index); 0935 0936 SKGObjectBase obj = m_model->getObject(idxs); 0937 QString id = obj.getUniqueID(); 0938 if (obj.getTable().isEmpty()) { 0939 // This is a group 0940 id = obj.getAttribute(QStringLiteral("t_title")); 0941 } 0942 m_expandedNodes.push_back(id); 0943 } 0944 0945 if (m_autoResize) { 0946 resizeColumnsToContentsDelayed(); 0947 } 0948 } 0949 0950 void SKGTreeView::expandAll() 0951 { 0952 SKGTRACEINFUNC(10) 0953 QTreeView::expandAll(); 0954 0955 if (m_autoResize) { 0956 resizeColumnsToContentsDelayed(); 0957 } 0958 } 0959 0960 void SKGTreeView::onCollapse(const QModelIndex& index) 0961 { 0962 SKGTRACEINFUNC(10) 0963 if (index.isValid() && (m_model != nullptr)) { 0964 QModelIndex idxs = (m_proxyModel != nullptr ? m_proxyModel->mapToSource(index) : index); 0965 0966 SKGObjectBase obj = m_model->getObject(idxs); 0967 0968 QString id = obj.getUniqueID(); 0969 if (obj.getTable().isEmpty()) { 0970 // This is a group 0971 id = obj.getAttribute(QStringLiteral("t_title")); 0972 } 0973 m_expandedNodes.removeOne(id); 0974 } 0975 0976 if (m_autoResize) { 0977 resizeColumnsToContentsDelayed(); 0978 } 0979 } 0980 0981 void SKGTreeView::onClick(const QModelIndex& index) 0982 { 0983 SKGTRACEINFUNC(10) 0984 if (index.isValid() && (m_actExpandAll != nullptr) && m_actExpandAll->isVisible()) { 0985 this->setExpanded(index, !this->isExpanded(index)); 0986 } 0987 } 0988 0989 void SKGTreeView::copy() 0990 { 0991 QItemSelectionModel* selection = selectionModel(); 0992 if (selection != nullptr) { 0993 QModelIndexList indexes = selection->selectedIndexes(); 0994 0995 if (indexes.empty()) { 0996 return; 0997 } 0998 0999 std::sort(indexes.begin(), indexes.end()); 1000 1001 QStringList colums_title; 1002 colums_title.reserve(indexes.count()); 1003 SKGStringListList columns_values; 1004 for (const auto& current : qAsConst(indexes)) { 1005 auto header_title = model()->headerData(current.column(), Qt::Horizontal).toString(); 1006 if (header_title.isEmpty()) { 1007 header_title = SKGServices::splitCSVLine(model()->headerData(current.column(), Qt::Horizontal, Qt::UserRole).toString(), QLatin1Char('|')).at(0); 1008 } 1009 1010 auto pos = colums_title.indexOf(header_title); 1011 if (pos == -1) { 1012 colums_title.append(header_title); 1013 colums_title.append(header_title + QLatin1String("_raw")); 1014 1015 columns_values.append(QStringList()); 1016 columns_values.append(QStringList()); 1017 1018 pos = colums_title.count() - 2; 1019 } 1020 1021 columns_values[pos].append(model()->data(current).toString()); 1022 columns_values[pos + 1].append(model()->data(current, Qt::UserRole).toString()); 1023 } 1024 1025 // Remove useless raw columns (when all raw values = displayed values) 1026 int nbCols = columns_values.count(); 1027 int nbRows = 0; 1028 if (nbCols > 0) { 1029 nbRows = columns_values.at(0).count(); 1030 for (int c = nbCols - 1; c >= 0; c = c - 2) { 1031 bool allEqual = true; 1032 bool allDisplayedEmpty = true; 1033 auto displayedValues = columns_values.at(c - 1); 1034 auto rawValues = columns_values.at(c); 1035 for (int r = 0; r < nbRows; ++r) { 1036 if (!displayedValues.at(r).isEmpty()) { 1037 allDisplayedEmpty = false; 1038 } 1039 if (rawValues.at(r) != displayedValues.at(r)) { 1040 allEqual = false; 1041 } 1042 } 1043 1044 // Remove the raw column 1045 if (allEqual) { 1046 // Remove the useless raw column 1047 colums_title.removeAt(c); 1048 columns_values.removeAt(c); 1049 nbCols--; 1050 } else if (allDisplayedEmpty) { 1051 // Remove the empty column and keep the raw version 1052 colums_title.removeAt(c - 1); 1053 columns_values.removeAt(c - 1); 1054 nbCols--; 1055 } 1056 } 1057 } 1058 1059 // Build text 1060 QString text = colums_title.join(QLatin1Char(';')) + QLatin1Char('\n'); 1061 if (nbCols > 0) { 1062 for (int r = 0; r < nbRows; ++r) { 1063 for (int c = 0; c < nbCols; ++c) { 1064 if (c != 0) { 1065 text += QLatin1Char(';'); 1066 } 1067 text += columns_values.at(c).at(r); 1068 } 1069 text += QLatin1Char('\n'); 1070 } 1071 } 1072 1073 auto clipBoard = QApplication::clipboard(); 1074 if (clipBoard != nullptr) { 1075 clipBoard->setText(text); 1076 } 1077 } 1078 } 1079 1080 void SKGTreeView::setZoomPosition(int iZoomPosition) 1081 { 1082 int newZoomPos = qMax(qMin(iZoomPosition, 10), -10); 1083 if (newZoomPos != zoomPosition() && m_fontOriginalPointSize + newZoomPos > 1) { 1084 QFont newFont = this->font(); 1085 newFont.setPointSize(m_fontOriginalPointSize + newZoomPos); 1086 int newIconSize = qMax(m_iconOriginalSize + newZoomPos, 1); 1087 1088 this->setFont(newFont); 1089 this->setIconSize(QSize(newIconSize, newIconSize)); 1090 header()->setIconSize(QSize(newIconSize, newIconSize)); 1091 1092 if (m_autoResize) { 1093 resizeColumnsToContentsDelayed(); 1094 } 1095 1096 Q_EMIT zoomChanged(newZoomPos); 1097 } 1098 } 1099 1100 int SKGTreeView::zoomPosition() 1101 { 1102 return this->font().pointSize() - m_fontOriginalPointSize; 1103 } 1104 1105 bool SKGTreeView::eventFilter(QObject* iObject, QEvent* iEvent) 1106 { 1107 if (iObject == this && iEvent != nullptr && iEvent->type() == QEvent::Wheel) { 1108 auto* e = dynamic_cast<QWheelEvent*>(iEvent); 1109 if (m_textResizable && (e != nullptr) && ((QApplication::keyboardModifiers() &Qt::ControlModifier) != 0u)) { 1110 int numDegrees = e->angleDelta().y() / 8; 1111 int numTicks = numDegrees / 15; 1112 1113 setZoomPosition(zoomPosition() + (numTicks > 0 ? 1 : -1)); 1114 e->setAccepted(true); 1115 return true; 1116 } 1117 } 1118 if (iObject == this && iEvent != nullptr && iEvent->type() == QEvent::KeyPress) { 1119 auto* kevent = dynamic_cast<QKeyEvent*>(iEvent); 1120 if (kevent != nullptr) { 1121 if (kevent->matches(QKeySequence::Copy) && this->state() != QAbstractItemView::EditingState) { 1122 copy(); 1123 if (iEvent != nullptr) { 1124 iEvent->accept(); 1125 } 1126 return true; // stop the process 1127 } 1128 } 1129 } 1130 return QTreeView::eventFilter(iObject, iEvent); 1131 } 1132 1133 void SKGTreeView::mousePressEvent(QMouseEvent* iEvent) 1134 { 1135 if ((iEvent != nullptr) && iEvent->button() == Qt::LeftButton && !(this->indexAt(iEvent->pos()).isValid())) { 1136 Q_EMIT clickEmptyArea(); 1137 clearSelection(); 1138 } 1139 1140 if ((iEvent != nullptr) && iEvent->button() == Qt::LeftButton && (m_proxyModel != nullptr) && (m_model != nullptr)) { 1141 int propertyUUID = m_proxyModel->data(indexAt(iEvent->pos()), 101).toInt(); 1142 if (propertyUUID != 0) { 1143 SKGPropertyObject prop(m_model->getDocument(), propertyUUID); 1144 QDesktopServices::openUrl(prop.getUrl(true)); 1145 } 1146 } 1147 1148 QTreeView::mousePressEvent(iEvent); 1149 } 1150 1151 bool SKGTreeView::isTextResizable() const 1152 { 1153 return m_textResizable; 1154 } 1155 1156 void SKGTreeView::setTextResizable(bool resizable) 1157 { 1158 if (m_textResizable != resizable) { 1159 m_textResizable = resizable; 1160 Q_EMIT modified(); 1161 } 1162 } 1163 1164 QTextBrowser* SKGTreeView::getTextBrowser() const 1165 { 1166 auto output = new QTextBrowser(); 1167 QTextCursor tcursor = output->textCursor(); 1168 tcursor.beginEditBlock(); 1169 1170 // Create table format 1171 QTextTableFormat tableFormat; 1172 tableFormat.setAlignment(Qt::AlignHCenter); 1173 tableFormat.setAlignment(Qt::AlignLeft); 1174 tableFormat.setBackground(QColor(255, 255, 255)); 1175 tableFormat.setCellPadding(5); 1176 tableFormat.setCellSpacing(5); 1177 1178 // Create table 1179 SKGStringListList table = getTable(); 1180 int nbRows = table.count(); 1181 int nbCol = table.at(0).count(); 1182 1183 QTextTable* tableau = tcursor.insertTable(nbRows, nbCol, tableFormat); 1184 1185 // Create frame 1186 QTextFrame* frame = tcursor.currentFrame(); 1187 QTextFrameFormat frameFormat = frame->frameFormat(); 1188 frameFormat.setBorder(0); 1189 frame->setFrameFormat(frameFormat); 1190 1191 // Create header table format 1192 QTextCharFormat headerFormat; 1193 headerFormat.setFontPointSize(6); 1194 headerFormat.setFontWeight(QFont::Bold); 1195 1196 // Create text format 1197 QTextCharFormat textFormat; 1198 textFormat.setFontPointSize(6); 1199 1200 // Create header 1201 for (int r = 0; r < nbRows; ++r) { 1202 const QStringList& line = table.at(r); 1203 for (int c = 0 ; c < nbCol ; ++c) { 1204 QTextCursor cellCursor = tableau->cellAt(r, c).firstCursorPosition(); 1205 cellCursor.insertText(line.at(c), (r == 0 ? headerFormat : textFormat)); 1206 } 1207 } 1208 1209 // End 1210 tcursor.endEditBlock(); 1211 1212 return output; 1213 } 1214 1215 SKGStringListList SKGTreeView::getTable(const QModelIndex& iIndex) const 1216 { 1217 // Build table 1218 SKGStringListList table; 1219 1220 // Get header names 1221 if (m_model != nullptr) { 1222 // Header 1223 int nb = m_model->columnCount(); 1224 int nb2 = m_model->rowCount(iIndex); 1225 table.reserve(1 + nb2 * 2); 1226 if (!iIndex.isValid()) { 1227 QStringList cols; 1228 cols.reserve(nb); 1229 for (int i = 0; i < nb; ++i) { 1230 cols.append(m_model->headerData(i, Qt::Horizontal, Qt::UserRole).toString().split('|').at(0)); 1231 } 1232 table.append(cols); 1233 } 1234 1235 // Get content 1236 for (int i = 0; i < nb2; ++i) { 1237 QStringList row; 1238 row.reserve(nb); 1239 for (int j = 0; j < nb; j++) { 1240 // We have to check the type for 214849 1241 QModelIndex idx = m_model->index(i, j, iIndex); 1242 1243 SKGServices::AttributeType type = m_model->getAttributeType(j); 1244 QString display = m_model->data(idx, type == SKGServices::FLOAT || m_model->getObject(idx).getTable().isEmpty() ? Qt::DisplayRole : Qt::UserRole).toString(); 1245 if (display.isEmpty()) { 1246 display = m_model->data(idx, Qt::DisplayRole).toString(); 1247 } 1248 row.append(display); 1249 } 1250 1251 table.append(row); 1252 1253 QModelIndex idx0 = m_model->index(i, 0, iIndex); 1254 if (m_model->hasChildren(idx0)) { 1255 table.append(getTable(idx0)); 1256 } 1257 } 1258 } 1259 return table; 1260 } 1261 1262 SKGError SKGTreeView::exportInFile(const QString& iFileName) 1263 { 1264 SKGError err; 1265 _SKGTRACEINFUNC(10) 1266 QString codec = QTextCodec::codecForLocale()->name(); 1267 QString extension = QFileInfo(iFileName).suffix().toUpper(); 1268 if (extension == QStringLiteral("CSV")) { 1269 // Write file 1270 QSaveFile file(iFileName); 1271 if (!file.open(QIODevice::WriteOnly)) { 1272 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", iFileName)); 1273 } else { 1274 QTextStream out(&file); 1275 out.setCodec(codec.toLatin1().constData()); 1276 QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_CSV); 1277 int nbl = dump.count(); 1278 for (int i = 0; i < nbl; ++i) { 1279 out << dump.at(i) << SKGENDL; 1280 } 1281 1282 // Close file 1283 file.commit(); 1284 } 1285 } else if (extension == QStringLiteral("PDF")) { 1286 QImage image(this->size(), QImage::Format_ARGB32); 1287 QPainter painter(&image); 1288 this->render(&painter); 1289 painter.end(); 1290 1291 { 1292 QPrinter printer(QPrinter::HighResolution); 1293 printer.setOutputFileName(iFileName); 1294 QPainter newPainter(&printer); 1295 1296 QRect painterRect = newPainter.viewport(); 1297 QSize imageSize = image.size(); 1298 imageSize.scale(painterRect.size(), Qt::KeepAspectRatio); 1299 newPainter.setViewport(painterRect.x(), painterRect.y(), imageSize.width(), imageSize.height()); 1300 newPainter.setWindow(image.rect()); 1301 newPainter.drawImage(0, 0, image); 1302 newPainter.end(); 1303 } 1304 } else if (extension == QStringLiteral("SVG")) { 1305 QSvgGenerator generator; 1306 generator.setFileName(iFileName); 1307 generator.setTitle(i18nc("Title of the content SVG export", "Skrooge SVG export")); 1308 generator.setDescription(i18nc("Description of the content SVG export", "A SVG drawing created by the Skrooge.")); 1309 1310 QPainter painter(&generator); 1311 QWidget* w = this->viewport(); 1312 w->render(&painter); 1313 generator.setSize(QSize(w->widthMM(), w->heightMM())); 1314 generator.setViewBox(QRect(0, 0, w->widthMM(), w->heightMM())); 1315 1316 painter.end(); 1317 } else if (extension == QStringLiteral("HTML")) { 1318 // Write file 1319 QSaveFile file(iFileName); 1320 if (!file.open(QIODevice::WriteOnly)) { 1321 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", iFileName)); 1322 } else { 1323 QTextStream out(&file); 1324 out.setCodec(codec.toLatin1().constData()); 1325 QTextBrowser* tb = getTextBrowser(); 1326 if (tb != nullptr) { 1327 out << tb->toHtml().replace(QStringLiteral("<meta name=\"qrichtext\" content=\"1\" />"), QStringLiteral("<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />")) << SKGENDL; 1328 1329 delete tb; 1330 } 1331 1332 // Close file 1333 file.commit(); 1334 } 1335 } else if (extension == QStringLiteral("ODT")) { 1336 QTextBrowser* tb = getTextBrowser(); 1337 if (tb != nullptr) { 1338 QTextDocument doc; 1339 doc.setHtml(tb->toHtml()); 1340 1341 QTextDocumentWriter docWriter(iFileName); 1342 docWriter.write(&doc); 1343 1344 delete tb; 1345 } 1346 } else { 1347 // Write file 1348 QSaveFile file(iFileName); 1349 if (!file.open(QIODevice::WriteOnly)) { 1350 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", iFileName)); 1351 } else { 1352 QTextStream out(&file); 1353 out.setCodec(codec.toLatin1().constData()); 1354 QStringList dump = SKGServices::tableToDump(getTable(), SKGServices::DUMP_TEXT); 1355 int nbl = dump.count(); 1356 for (int i = 0; i < nbl; ++i) { 1357 out << dump.at(i) << SKGENDL; 1358 } 1359 1360 // Close file 1361 file.commit(); 1362 } 1363 } 1364 1365 return err; 1366 } 1367 1368 void SKGTreeView::onExport() 1369 { 1370 _SKGTRACEINFUNC(10) 1371 QString fileName = SKGMainPanel::getSaveFileName(QStringLiteral("kfiledialog:///IMPEXP"), QStringLiteral("text/csv text/plain text/html application/vnd.oasis.opendocument.text image/svg+xml application/pdf"), this); 1372 if (!fileName.isEmpty()) { 1373 SKGError err = exportInFile(fileName); 1374 SKGMainPanel::displayErrorMessage(err); 1375 QDesktopServices::openUrl(QUrl::fromLocalFile(fileName)); 1376 } 1377 } 1378 1379 void SKGTreeView::setModel(QAbstractItemModel* iModel) 1380 { 1381 if (iModel != this->model()) { 1382 m_model = qobject_cast<SKGObjectModelBase*>(iModel); 1383 m_proxyModel = qobject_cast<SKGSortFilterProxyModel*> (iModel); 1384 if (m_proxyModel != nullptr) { 1385 m_model = qobject_cast<SKGObjectModelBase*>(m_proxyModel->sourceModel()); 1386 } 1387 1388 if (m_model != nullptr) { 1389 connect(m_model, &SKGObjectModelBase::afterReset, this, &SKGTreeView::setupHeaderMenu); 1390 // connect(m_model, &SKGObjectModelBase::afterReset, this, &SKGTreeView::onSelectionChanged); 1391 connect(m_model, &SKGObjectModelBase::afterReset, this, &SKGTreeView::respanFirstColumns, Qt::QueuedConnection); 1392 } 1393 QTreeView::setModel(iModel); 1394 1395 rebuildContextualMenu(); 1396 refreshExpandCollapse(); 1397 } 1398 } 1399 1400 QMenu* SKGTreeView::getHeaderMenu() const 1401 { 1402 return m_headerMenu; 1403 } 1404 1405 void SKGTreeView::rebuildContextualMenu() 1406 { 1407 // Remove all Actions 1408 const auto list = actions(); 1409 for (auto act : list) { 1410 removeAction(act); 1411 } 1412 1413 if (selectionMode() != NoSelection) { 1414 // Build contextual menu 1415 this->insertAction(nullptr, m_actCopy); 1416 this->insertAction(nullptr, m_actExpandAll); 1417 this->insertAction(nullptr, m_actCollapseAll); 1418 1419 if ((m_model != nullptr) && (SKGMainPanel::getMainPanel() != nullptr)) { 1420 const auto list = SKGMainPanel::getMainPanel()->getActionsForContextualMenu(m_model->getRealTable()); 1421 for (const auto& act : list) { 1422 if (act == nullptr) { 1423 insertGlobalAction(); 1424 } else { 1425 insertAction(nullptr, act); 1426 } 1427 } 1428 } 1429 } 1430 }