/** * @RDE_HEADER@ * LtsDataView is the exported View, which contains and manages a Custom View and a Table View * $Id$ * $HeadURL$ * $Author$ * * Created by Mike Wille, SLSE, 2016-06-15 */ #include "lts_data_view.h" #include "ui_lts_data_view.h" #include "search_model.h" #include "abstract_display_widget.h" #include "tree_view.h" #include "tree_view_delegate.h" #include "icon_view_delegate.h" #include "data_delegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// get the list of visual index as they are display namespace LtsDataView { static const QString s_columSettingSuffix = QStringLiteral("_ColSetting"); static const QString s_colSettingIndexStr = QStringLiteral("index"); static const QString s_colSettingVisiStr = QStringLiteral("visible"); static const QString s_ColumnOrganiserSettingStr = QStringLiteral("ColumnOrganiserSetting"); #include LtsDataView::LtsDataView(QWidget *parent) : QWidget(parent) , m_ui(new Ui::LtsDataView) , m_columnFilterProxy(nullptr) , m_searchModel(new SearchModel(this)) , m_bOwnProxy(false) { m_ui->setupUi(this); m_ui->tableView->setAlternatingRowColors(true); m_ui->tableView->setAllColumnsShowFocus(true); m_ui->tbColumnOrganiser->setVisible(false); m_ui->tbColumnOrganiser->setEnabled(false); #ifndef LTS_DEBUG m_ui->lblRows->setHidden(true); // m_ui->tbCsvExport->hide(); #endif m_ui->searchView->setItemDelegate(new TreeViewDelegate(this)); m_ui->tableView->setItemDelegate(new DataDelegate(this)); connect(m_ui->tbCustom, &QPushButton::clicked, this, &LtsDataView::LtsDataView::selectCustomView); connect(m_ui->tbTable, &QPushButton::clicked, this, &LtsDataView::LtsDataView::selectTableView); connect(m_ui->tableView, &TreeView::signalCopyToClipBoard, this, &LtsDataView::LtsDataView::slotCopyToClipboard, Qt::QueuedConnection); // event should pass before this slot can be entered connect(m_ui->tbCopy, &QPushButton::clicked, this, &LtsDataView::LtsDataView::slotCopyToClipboard); connect(m_ui->tbCsvExport, &QPushButton::clicked, this, &LtsDataView::LtsDataView::slotCsvExport); connect(m_ui->tbColumnOrganiser, &QPushButton::clicked, this, &LtsDataView::LtsDataView::slotColumnOrganiserDialog); #ifdef TABLET_MODE const QList scrollWidgets = findChildren(); for (QWidget *widget : scrollWidgets) { QVariant isConst = widget->property("const"); if (isConst.isValid() && isConst.toBool()) continue; addTouchScroll(widget); } #endif selectTableView(); } LtsDataView::~LtsDataView() { delete m_ui; } void LtsDataView::setModel(QAbstractItemModel *model, const std::function &customDisplayWidgetCreator) { Q_ASSERT(model); if (!customDisplayWidgetCreator) // no custom widgets? pretend to be a pure table view by hiding the ability to swap between views { m_ui->tbCustom->setEnabled(false); // disable custom view m_ui->tbCustom->hide(); m_ui->tbTable->hide(); m_ui->line->hide(); selectTableView(); } else { IconViewDelegate *delegate = new IconViewDelegate(this); delegate->setDisplayWidgetCreator(customDisplayWidgetCreator); m_ui->customView->setItemDelegate(delegate); // does not take ownership of delegate ... } LtsWidgets::LtsColumnFilterProxyModel *proxy = qobject_cast(model); if (m_bOwnProxy) delete m_columnFilterProxy; if (proxy) { m_columnFilterProxy = proxy; m_bOwnProxy = false; } else { Q_ASSERT_X(!qobject_cast(model), __FUNCTION__, "Don't pass plain QSortFilterProxyModel to LtsDataView::setModel - it will not work as expected. Derive from LtsColumnFilterProxyModel instead"); m_columnFilterProxy = new LtsWidgets::LtsColumnFilterProxyModel(this); m_columnFilterProxy->setSourceModel(model); m_bOwnProxy = true; } m_searchModel->setSourceModel(model); connect(m_searchModel, &SearchModel::stringFilterForColumnChanged, m_columnFilterProxy, &LtsWidgets::LtsColumnFilterProxyModel::onSetColumnStringFilter); connect(m_searchModel, &SearchModel::dataFilterForColumnChanged, m_columnFilterProxy, &LtsWidgets::LtsColumnFilterProxyModel::onSetColumnDataFilter); m_ui->searchView->setModel(m_searchModel); m_ui->tableView->setModel(m_columnFilterProxy); m_ui->customView->setModel(m_columnFilterProxy); m_ui->searchView->show(); // Main Model connect(m_columnFilterProxy, &QAbstractItemModel::rowsInserted, this, &LtsDataView::slotRowCountChanged); connect(m_columnFilterProxy, &QAbstractItemModel::rowsRemoved, this, &LtsDataView::slotRowCountChanged); connect(m_columnFilterProxy, &QAbstractItemModel::modelReset, this, &LtsDataView::slotRowCountChanged); connect(m_columnFilterProxy->sourceModel(), &QAbstractItemModel::rowsInserted, this, &LtsDataView::slotRowCountChanged); connect(m_columnFilterProxy->sourceModel(), &QAbstractItemModel::rowsRemoved, this, &LtsDataView::slotRowCountChanged); connect(m_columnFilterProxy->sourceModel(), &QAbstractItemModel::modelReset, this, &LtsDataView::slotRowCountChanged); // Selection Changed // for example needed by OperatorDialog::enaDisa : connect(m_ui->customView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &LtsDataView::signalSelectionChanged); connect(m_ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &LtsDataView::signalSelectionChanged); // : for example needed by OperatorDialog::enaDisa connect(m_ui->customView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this](const QItemSelection &s, const QItemSelection &d) { emit selectionChanged(mapSelectionToSource(s), mapSelectionToSource(d)); }); connect(m_ui->tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this](const QItemSelection &s, const QItemSelection &d) { emit selectionChanged(mapSelectionToSource(s), mapSelectionToSource(d)); }); // Activated connect(m_ui->customView, &QAbstractItemView::activated, this, [this](const QModelIndex &idx) { emit activated(mapToSource(idx)); }); connect(m_ui->tableView, &QAbstractItemView::activated, this, [this](const QModelIndex &idx) { emit activated(mapToSource(idx)); }); // Entered (requires MouseTracking) m_ui->customView->setMouseTracking(true); connect(m_ui->customView, &QAbstractItemView::entered, this, [this](const QModelIndex &index) { emit signalEntered(mapToSource(index)); }); m_ui->tableView->setMouseTracking(true); connect(m_ui->tableView, &QAbstractItemView::entered, this, [this](const QModelIndex &index) { emit signalEntered(mapToSource(index)); }); // Clicked connect(m_ui->customView, &QAbstractItemView::clicked, this, [this](const QModelIndex &idx) { emit clicked(mapToSource(idx)); }); connect(m_ui->tableView, &QAbstractItemView::clicked, this, [this](const QModelIndex &idx) { emit clicked(mapToSource(idx)); }); // Double Clicked connect(m_ui->customView, &QAbstractItemView::doubleClicked, this, &LtsDataView::signalDoubleClicked); connect(m_ui->tableView, &QAbstractItemView::doubleClicked, this, &LtsDataView::signalDoubleClicked); connect(m_ui->customView, &QAbstractItemView::doubleClicked, this, [this](const QModelIndex &idx) { emit doubleClicked(mapToSource(idx)); }); connect(m_ui->tableView, &QAbstractItemView::doubleClicked, this, [this](const QModelIndex &idx) { emit doubleClicked(mapToSource(idx)); }); // Search View connect(m_ui->searchView->header(), &QHeaderView::sortIndicatorChanged, this, &LtsDataView::slotSetSortIndicator); connect(m_searchModel, &SearchModel::signalFilterChanged, this, &LtsDataView::slotRowCountChanged); connect(m_searchModel, &SearchModel::signalFilterChanged, this, [this](){ emit filterChanged(); }); connect(m_ui->searchView->header(), &QHeaderView::sectionResized, this, &LtsDataView::slotSectionResized); connect(m_ui->searchView->header(), &QHeaderView::sectionMoved, this, &LtsDataView::slotHeaderMoved); connect(m_ui->searchView->header(), &QHeaderView::sectionHandleDoubleClicked, this, &LtsDataView::slotResizeColumn, Qt::QueuedConnection); // wait until the header did a resize by header text // Horizontal Scrollbar connect(m_ui->searchView->horizontalScrollBar(), &QScrollBar::rangeChanged, this, &LtsDataView::slotSetHorizontalRange, Qt::QueuedConnection); // queued to make sure pageStep is up-to-date connect(m_ui->searchView->horizontalScrollBar(), &QScrollBar::sliderMoved, this, &LtsDataView::slotSetHorizontalValue); connect(m_ui->searchView->horizontalScrollBar(), &QScrollBar::valueChanged, this, &LtsDataView::slotSetHorizontalValue); connect(m_ui->horizontalScrollBar, &QScrollBar::sliderMoved, m_ui->searchView->horizontalScrollBar(), &QScrollBar::setValue); connect(m_ui->horizontalScrollBar, &QScrollBar::sliderMoved, m_ui->tableView->horizontalScrollBar(), &QScrollBar::setValue); connect(m_ui->horizontalScrollBar, &QScrollBar::valueChanged, m_ui->searchView->horizontalScrollBar(), &QScrollBar::setValue); // when wheelEvent'ing on the scrollbar connect(m_ui->horizontalScrollBar, &QScrollBar::valueChanged, m_ui->tableView->horizontalScrollBar(), &QScrollBar::setValue); // when wheelEvent'ing on the scrollbar connect(m_ui->tableView->horizontalScrollBar(), &QScrollBar::rangeChanged, this, &LtsDataView::slotSetHorizontalRange, Qt::QueuedConnection); // queued to make sure pageStep is up-to-date connect(m_ui->tableView->horizontalScrollBar(), &QScrollBar::sliderMoved, this, &LtsDataView::slotSetHorizontalValue); connect(m_ui->tableView->horizontalScrollBar(), &QScrollBar::valueChanged, this, &LtsDataView::slotSetHorizontalValue); // when wheelEvent'ing on the scrollbar // custom view has no horizontal scroll bar // Vertical Scrollbar connect(m_ui->customView->verticalScrollBar(), &QScrollBar::rangeChanged, this, &LtsDataView::slotSetVerticalRange, Qt::QueuedConnection); // queued to make sure pageStep is up-to-date connect(m_ui->customView->verticalScrollBar(), &QScrollBar::sliderMoved, this, &LtsDataView::slotSetVerticalValue); connect(m_ui->customView->verticalScrollBar(), &QScrollBar::valueChanged, this, &LtsDataView::slotSetVerticalValue); // custom view uses setValue on wheelEvent, listen to that connect(m_ui->tableView->verticalScrollBar(), &QScrollBar::rangeChanged, this, &LtsDataView::slotSetVerticalRange, Qt::QueuedConnection); // queued to make sure pageStep is up-to-date connect(m_ui->tableView->verticalScrollBar(), &QScrollBar::sliderMoved, this, &LtsDataView::slotSetVerticalValue); connect(m_ui->tableView->verticalScrollBar(), &QScrollBar::valueChanged, this, &LtsDataView::slotSetVerticalValue); // when wheelEvent'ing on the scrollbar connect(m_ui->verticalScrollBar, &QScrollBar::valueChanged, m_ui->tableView->verticalScrollBar(), &QScrollBar::setValue); connect(m_ui->verticalScrollBar, &QScrollBar::valueChanged, m_ui->customView->verticalScrollBar(), &QScrollBar::setValue); // search view has no vertical scroll bar // for context menue: (but contextPolicy has to be set by user...) QTreeView *treeView = m_ui->tableView; connect(treeView, &QListView::customContextMenuRequested, this, &LtsDataView::onCustomContextMenuRequested, Qt::UniqueConnection); connect(m_ui->customView, &QListView::customContextMenuRequested, this, &LtsDataView::onCustomContextMenuRequested, Qt::UniqueConnection); // check if a column has a combobox search for (int c = 0; c < model->columnCount(); ++c) { const QModelIndex idx = m_searchModel->index(0, c, QModelIndex()); const QVariant v = model->headerData(c, Qt::Horizontal, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole); if (!v.isNull()) { m_searchModel->setData(idx, false, Qt::DecorationRole); m_searchModel->setData(idx, TreeViewDelegate::noFilterString(), Qt::DisplayRole); } } // set minimim size for this data view to display custom widgets properly // TODO: ! // AbstractDisplayWidget *dummy = customDisplayWidgetCreator(); // setMinimumSize(dummy->minimumWidth() + 40 + 100, dummy->minimumHeight() + 40 + 100); // FIXME: margin? slotRowCountChanged(); // show row count // hide h-scrollbar if not needed if (m_ui->searchView->horizontalScrollBar()->maximum() == 0) m_ui->horizontalScrollBar->hide(); setSortIndicator(0, Qt::AscendingOrder); resizeHeadersByContent(); if (m_autoResizeToConent) syncSearchHeaderAndTableContentColumnSize(); m_DebugModeColIds.clear(); } QAbstractItemModel *LtsDataView::sourceModel() const { return m_columnFilterProxy ? m_columnFilterProxy->sourceModel() : nullptr; } LtsWidgets::LtsColumnFilterProxyModel *LtsDataView::proxyModel() const { return m_columnFilterProxy; } QItemSelectionModel *LtsDataView::selectionModel() const { return m_ui->tableView->selectionModel(); } SearchModel *LtsDataView::searchModel() const { return m_searchModel; } QTreeView *LtsDataView::treeView() { return m_ui->tableView; } const QTreeView *LtsDataView::treeView() const { return m_ui->tableView; } QTreeView *LtsDataView::searchView() { return m_ui->searchView; } const QTreeView *LtsDataView::searchView() const { return m_ui->searchView; } QListView *LtsDataView::getCustomView() { return m_ui->customView; } QWidget *LtsDataView::customWidget() const { QLayout *hbl = m_ui->customWidget->layout(); if (hbl) { for (int i = 0; i < hbl->count(); ++i) { if (hbl->itemAt(i)->widget()) return hbl->itemAt(i)->widget(); } } return nullptr; } void LtsDataView::setCustomWidget(QWidget *customWidget) { QLayout *hbl = m_ui->customWidget->layout(); if (hbl) { while (hbl->count() > 0) delete hbl->takeAt(0)->widget(); } delete hbl; hbl = new QHBoxLayout(m_ui->customWidget); hbl->setMargin(0); hbl->addWidget(customWidget); m_ui->customWidget->setLayout(hbl); update(); // TODO: noetig? macht doch qt automatisch bei layout aenderungen? } QModelIndexList LtsDataView::selectedRows(int column) const { if (m_ui->stackedWidget->currentIndex() == TABLE) { if (m_ui->tableView->selectionModel()) return mapToSource(m_ui->tableView->selectionModel()->selectedRows(column)); } else { Q_ASSERT(column == 0); if (m_ui->customView->selectionModel()) return mapToSource(m_ui->customView->selectionModel()->selectedIndexes()); } return {}; } void LtsDataView::selectRow(const QModelIndex &idxSource) { QModelIndex idx = m_bOwnProxy ? m_columnFilterProxy->mapFromSource(idxSource) : idxSource; QItemSelectionModel *pSelection = m_ui->tableView->selectionModel(); if (idx.isValid() && pSelection) pSelection->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); } void LtsDataView::clearSelection() { if (m_ui->tableView) m_ui->tableView->clearSelection(); if (m_ui->customView) m_ui->customView->clearSelection(); } void LtsDataView::setSelectionMode(QAbstractItemView::SelectionMode selectionMode) { m_ui->tableView->setSelectionMode(selectionMode); m_ui->customView->setSelectionMode(selectionMode); } QAbstractItemView::SelectionMode LtsDataView::selectionMode() const { return m_ui->tableView->selectionMode(); } QModelIndex LtsDataView::currentIndex() const { return mapToSource(currentView()->currentIndex()); } void LtsDataView::setCurrentIndex(const QModelIndex &idxSource) { const QModelIndex &idx = mapFromSource(idxSource); QAbstractItemView *view = currentView(); view->setCurrentIndex(idx); view->scrollTo(idx, QTreeView::ScrollHint::PositionAtCenter); } void LtsDataView::scrollTo(const QModelIndex &idxSource) { QModelIndex idx = m_bOwnProxy ? m_columnFilterProxy->mapFromSource(idxSource) : idxSource; if (idx.isValid()) m_ui->tableView->scrollTo(idx, QAbstractItemView::PositionAtCenter); } void LtsDataView::moveSection(int from, int to) { m_ui->searchView->header()->moveSection(from, to); } void LtsDataView::seamless() { // TODO: FIXME: quick hack m_ui->tbCopy->setEnabled(false); m_ui->tbCsvExport->setEnabled(false); m_ui->tbCustom->setEnabled(false); m_ui->tbTable->setEnabled(false); delete m_ui->horizontalLayout; // does not delete its indirect children m_ui->horizontalLayout = nullptr; } void LtsDataView::setViewHeaderMovable(bool state) { m_ui->searchView->header()->setSectionsMovable(state); } /*! * \brief LtsDataView::saveTableSettings * \param defaultSetting: if user want to save default setting of column */ void LtsDataView::saveTableSettings(bool defaultSetting) { //qWarning()<<"########### [saveTableSettings]..."; QJsonArray jsArr; int colCount = m_ui->tableView->model()->columnCount(); for(int i=0; itableView->header()->logicalIndex(i); // if true save col default order reset visibility bool visible = defaultSetting ? true : !m_ui->searchView->isColumnHidden(logicalIndex); if (m_DebugModeColIds.contains(logicalIndex)) visible = false; // QJsonObject column; column[s_colSettingIndexStr] = logicalIndex; column[s_colSettingVisiStr] = visible; jsArr.append(column); } QJsonObject obj; obj.insert(m_tableObjIdentifier, jsArr); //qWarning()<<">>>[SAVE] Setting jsObj: -> "<setValue(columnOrganiserGroup(), identifier, QVariant::fromValue(obj)); } bool LtsDataView::restoreTableSettings() { //qCritical()<<"########### [restoreTableSettings]..."; LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance(); QJsonObject dataObj = opSettings->value(columnOrganiserGroup(), getOperatorColIdentifier()).toJsonObject(); if (dataObj.isEmpty()) { qCritical()<<">>> [restoreTableSettings] Object EMPTY!!"; return false; } QJsonArray jsArr = dataObj.value(m_tableObjIdentifier).toArray(); qWarning()<<">>> [restoreTableSettings] jsData: "<>> [restoreTableSettings] json array EMPTY!!"; return false; } int colIndex =0; for(const QJsonValue &value: jsArr) { QJsonObject column = value.toObject(); int index = column[s_colSettingIndexStr].toInt(); bool visible = column[s_colSettingVisiStr].toBool(); if (m_DebugModeColIds.contains(index)) visible = false; // hide debug columns //qWarning()<<"[restore]-> index: "<searchView->header()->visualIndex(index)<<" visi: "<searchView->header()->moveSection(m_ui->searchView->header()->visualIndex(index), colIndex++); m_ui->searchView->setColumnHidden(index, !visible); } return true; } /*! * \brief LtsDataView::setColumnViewFromSetting * This Method will is Call to set Column Table settings from what was saved in json */ void LtsDataView::setColumnViewFromSetting() { qWarning()<<"######### setColumnViewFromSetting ... "; m_userChangedColumnPos = false; // on internal move section avoid saving column setting bool brestore = restoreTableSettings(); if (!brestore) qCritical()<<" [setColumnViewFromSetting] couldn't restore table settings"; m_userChangedColumnPos = true; } QString LtsDataView::getOperatorColIdentifier() const { return m_tableObjIdentifier + s_columSettingSuffix; } QString LtsDataView::columnOrganiserGroup() const { return s_ColumnOrganiserSettingStr; } /*! * \brief LtsDataView::getColumnOrganiserSetting * \return get vector of struct from column jsondata (struct: id, visibility,..) */ QVector LtsDataView::getColumnOrganiserSetting() { const QString identifier = m_tableObjIdentifier + s_columSettingSuffix; LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance(); QJsonObject dataObj = opSettings->value(columnOrganiserGroup(), identifier).toJsonObject(); QJsonArray jsArr = dataObj.value(m_tableObjIdentifier).toArray(); QVector vecInfo; vecInfo.reserve(jsArr.count()); for(const QJsonValue &value: jsArr) { ColumnOrganiserInfo ci; QJsonObject column = value.toObject(); ci.logicalIndex = column[s_colSettingIndexStr].toInt(); ci.show = column[s_colSettingVisiStr].toBool(); vecInfo.push_back(ci); } return vecInfo; } /*! * \brief LtsDataView::setColumnsOrganiserEnabled * \param tableIdentifier: unique name for table using column Settings * \param enable : set button for column Setting Widget visible */ void LtsDataView::setColumnsOrganiserEnabled(const QString &tableIdentifier, bool enable) { qWarning()<<"############## [setColumnsOrganiserEnabled]..."; m_tableObjIdentifier = tableIdentifier; m_ui->tbColumnOrganiser->setVisible(enable); m_ui->tbColumnOrganiser->setEnabled(enable); if (!restoreTableSettings()) saveTableSettings(); m_userChangedColumnPos = enable; } /*! * \brief LtsDataView::slotColumnOrganiserDialog */ void LtsDataView::slotColumnOrganiserDialog() { qWarning()<<"############# [slotColumnOrganiserDialog]..."; if (!m_ui->tbColumnOrganiser->isEnabled()) return; auto vecInfo = getColumnOrganiserSetting(); if(vecInfo.isEmpty()) { LtsWidgets::LtsMessageDialog::showMessage(this, tr("Couldn't start Column Organiser Dialog"), tr("Column Organiser")); return; } const int colCount = m_columnFilterProxy->columnCount(); /// set Column names for Dialog Widget for( auto &ci : vecInfo) { if (ci.logicalIndex > colCount) continue; ci.name = m_columnFilterProxy->headerData(ci.logicalIndex, Qt::Horizontal).toString(); // hide columns displayed only in debug mode // because column is marked for debug, user will not be able to hide or show this column if (m_DebugModeColIds.contains(ci.logicalIndex)) { ci.show = false; ci.debugOnly = true; } } LtsColumnsSettingWidget *dialog = new LtsColumnsSettingWidget(vecInfo, this); //LtsColumnOrganiser *dialog = new LtsColumnOrganiser(vecInfo, this); connect(dialog, &LtsColumnsSettingWidget::columnVisibilityChanged, this, &LtsDataView::updateColumnsVisibility); connect(dialog, &LtsColumnsSettingWidget::columnsResetted, this, &LtsDataView::slotSetColumnsDefaultOrder); dialog->setModal(true); dialog->show(); } void LtsDataView::setRootIsDecorated(bool isDecorated) { if (m_ui && m_ui->tableView) m_ui->tableView->setRootIsDecorated(isDecorated); } bool LtsDataView::rootIsDecorated() const { if (m_ui && m_ui->tableView) return m_ui->tableView->rootIsDecorated(); return false; } void LtsDataView::setAlternatingRowColors(bool enable) { m_ui->tableView->setAlternatingRowColors(enable); } bool LtsDataView::alternatingRowColors() const { return m_ui->tableView->alternatingRowColors(); } void LtsDataView::setColumnCountVisible(bool bVisible) { m_ui->lblRows->setVisible(bVisible); m_ui->lbRows->setVisible(bVisible); } bool LtsDataView::showColumnCount() const { return m_ui->lblRows->isVisible(); } void LtsDataView::setToolButtonsVisible(bool bVisible) { m_ui->tbCsvExport->setVisible(bVisible); m_ui->tbCopy->setVisible(bVisible); m_ui->tbTable->setVisible(bVisible); m_ui->tbCustom->setVisible(bVisible); m_ui->line->setVisible(bVisible); } bool LtsDataView::toolButtonsVisible() const { return m_ui->tbCsvExport->isVisible() && m_ui->tbCopy->isVisible() && m_ui->tbTable->isVisible() && m_ui->tbCustom->isVisible(); } void LtsDataView::setAllColumnsShowFocus(bool enable) { m_ui->tableView->setAllColumnsShowFocus(enable); } bool LtsDataView::allColumnsShowFocus() const { return m_ui->tableView->allColumnsShowFocus(); } void LtsDataView::hideColumn(int column, bool debugMode) { if (debugMode) m_DebugModeColIds.insert(column); // used in setting column order and visibility setColumnHidden(column, true); } void LtsDataView::showColumn(int column) { setColumnHidden(column, false); } bool LtsDataView::isColumnHidden(int column) const { return m_ui->searchView->isColumnHidden(column); } void LtsDataView::setColumnHidden(int column, bool hide) { m_ui->searchView->setColumnHidden(column, hide); m_ui->tableView->setColumnHidden(column, hide); } void LtsDataView::setAutoResizeToContent(bool enable) { m_autoResizeToConent = enable; } bool LtsDataView::autoResizeToContent() const { return m_autoResizeToConent; } void LtsDataView::resizeSection(int logicalIndex, int size) { m_ui->searchView->header()->resizeSection(logicalIndex, size); } void LtsDataView::resizeHeadersByContent() { for (int i = 0; i < m_ui->tableView->header()->count(); i++) slotResizeColumn(i); } void LtsDataView::resizeColumnsToContent() { if (!m_columnFilterProxy) return; // Sync von Header und Table erfolgt mit Aufruf von syncSearchHeaderAndTableContentColumnSize() nachdem das Modell initialisiert wurde. const int colCount = m_columnFilterProxy->columnCount(); for (int col = 0; col < colCount; ++col) m_ui->tableView->header()->setSectionResizeMode(col, QHeaderView::ResizeToContents); } void LtsDataView::syncSearchHeaderAndTableContentColumnSize() { m_ui->searchView->header()->resizeSections(QHeaderView::ResizeToContents); const int colCount = m_columnFilterProxy->columnCount(); for (int col = 0; col < colCount; ++col) slotResizeColumn(col); } void LtsDataView::setRootIndex(const QModelIndex &srcIndex) { m_ui->searchView->setRootIndex((srcIndex)); m_ui->tableView->setRootIndex(m_columnFilterProxy->mapFromSource(srcIndex)); m_ui->customView->setRootIndex(m_columnFilterProxy->mapFromSource(srcIndex)); } void LtsDataView::setSortIndicator(int logicalIndex, Qt::SortOrder order) { m_ui->searchView->header()->setSortIndicator(logicalIndex, order); } void LtsDataView::setSortIndicatorShown(bool show) { m_ui->searchView->header()->setSortIndicatorShown(show); } Qt::ContextMenuPolicy LtsDataView::contextMenuPolicyPerView(LtsDataView::Page page) const { if (!m_ui) return Qt::NoContextMenu; switch (page) { case TABLE: if (m_ui->tableView) return m_ui->tableView->contextMenuPolicy(); break; case CUSTOM: if (m_ui->customView) return m_ui->customView->contextMenuPolicy(); } return Qt::NoContextMenu; } void LtsDataView::setContextMenuPolicyPerView(LtsDataView::Page page, Qt::ContextMenuPolicy policy) { if (!m_ui) return; switch (page) { case TABLE: if (m_ui->tableView) m_ui->tableView->setContextMenuPolicy(policy); break; case CUSTOM: if (m_ui->customView) m_ui->customView->setContextMenuPolicy(policy); break; } } QModelIndex LtsDataView::mapToSource(const QModelIndex &mi) const { return m_columnFilterProxy ? m_columnFilterProxy->mapToSource(mi) : mi; } QModelIndex LtsDataView::mapFromSource(const QModelIndex &mi) const { return m_columnFilterProxy ? m_columnFilterProxy->mapFromSource(mi) : mi; } QModelIndexList LtsDataView::mapToSource(const QModelIndexList &mil) const { QModelIndexList sourced; if (!m_columnFilterProxy) return sourced; sourced.reserve(mil.size()); for (const auto &mi : mil) sourced.append(mapToSource(mi)); return sourced; } QItemSelection LtsDataView::mapSelectionToSource(const QItemSelection &is) const { return m_columnFilterProxy ? m_columnFilterProxy->mapSelectionToSource(is) : is; } bool LtsDataView::setInitialSearchContent(int section, const QVariant &filterData) { if (!m_searchModel || !m_columnFilterProxy) { Q_ASSERT(false); return false; } const QVariant headerData = m_columnFilterProxy->headerData(section, Qt::Horizontal, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole); const LtsWidgets::ComboboxFilterContent cfc(LtsWidgets::ComboboxFilterContent::fromVariant(headerData)); if (!cfc.isValid()) { const QModelIndex index = m_searchModel->index(0, section, QModelIndex()); // assume it's a text search m_searchModel->setData(index, filterData, Qt::DisplayRole); m_searchModel->setData(index, filterData, Qt::EditRole); return false; } for (const auto &d : cfc.data) { if (d.data == filterData) { const QModelIndex index = m_searchModel->index(0, section, QModelIndex()); m_searchModel->setData(index, d.text, Qt::DisplayRole); m_searchModel->setData(index, d.data, LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole); return true; } } return false; } void LtsDataView::setIconSize(const QSize &size) { m_ui->tableView->setIconSize(size); } QByteArray LtsDataView::saveState() const { return m_ui->searchView->header()->saveState(); } bool LtsDataView::restoreState(const QByteArray &headerState) { bool ok = m_ui->searchView->header()->restoreState(headerState); if (ok) ok = m_ui->tableView->header()->restoreState(headerState); return ok; } QString LtsDataView::createCsv() const { if (!m_columnFilterProxy) return QString(); const QAbstractItemView *itemView = currentView(); const QModelIndexList selection = itemView->selectionModel()->selectedRows(); // create csv: int cols = m_columnFilterProxy->columnCount(); QString csv; QStringList csvLine; for (auto i = selection.constBegin(); i != selection.constEnd(); ++i) { int row = (*i).row(); csvLine.clear(); for (int k = 0; k < cols; k++) if (m_ui->stackedWidget->currentIndex() != TABLE || !m_ui->tableView->isColumnHidden(k)) csvLine.append(m_columnFilterProxy->data(m_columnFilterProxy->index(row, k)).toString()); csv += csvLine.join(QLatin1Char(';')); csv += QStringLiteral("\n"); // FIXME: windows? } return csv; } void LtsDataView::addTouchScroll(QWidget *widget) { if (!widget) return; widget->setAttribute(Qt::WA_AcceptTouchEvents); { QScrollArea *sca = qobject_cast(widget); if (sca) { QScroller::grabGesture(sca->viewport(), QScroller::LeftMouseButtonGesture); QScroller::grabGesture(sca, QScroller::LeftMouseButtonGesture); return; } } { QAbstractItemView *aiv = qobject_cast(widget); if (aiv) { QScroller::grabGesture(aiv->viewport(), QScroller::LeftMouseButtonGesture); QScroller::grabGesture(aiv, QScroller::LeftMouseButtonGesture); aiv->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); aiv->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); return; } } } QAbstractItemView *LtsDataView::currentView() const { if (m_ui->stackedWidget->currentIndex() == TABLE) return m_ui->tableView; return m_ui->customView; } void LtsDataView::keyPressEvent(QKeyEvent *event) // CustomView events land here { if (QGuiApplication::queryKeyboardModifiers() == Qt::ControlModifier && event->key() == Qt::Key_C) slotCopyToClipboard(); // Enter: Enter on numpad if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) emit signalEnterPressed(); QWidget::keyPressEvent(event); } void LtsDataView::selectTableView() { selectView(TABLE); } void LtsDataView::selectCustomView() { selectView(CUSTOM); } void LtsDataView::selectView(Page page) { m_ui->tbTable->setChecked(page == TABLE); m_ui->tbCustom->setChecked(page == CUSTOM); m_ui->stackedWidget->setCurrentIndex(page); slotSetVerticalRange(); slotSetVerticalValue(m_ui->customView->verticalScrollBar()->value()); } void LtsDataView::slotCopyToClipboard() { QApplication::clipboard()->setText(createCsv()); // finally copy to clipboard } void LtsDataView::collapseAll() { m_ui->tableView->collapseAll(); } void LtsDataView::expandAll() { m_ui->tableView->expandAll(); } void LtsDataView::expand(const QModelIndex &index) { m_ui->tableView->expand(index); } void LtsDataView::setSortingEnabled(bool enable) { m_ui->searchView->setSortingEnabled(enable); m_ui->tableView->setSortingEnabled(enable); } void LtsDataView::slotSetSortIndicator(int logicalIndex, Qt::SortOrder order) { m_ui->tableView->header()->setSortIndicator(logicalIndex, order); // TODO: needed since custom view rebuild?: m_ui->customView->update(); emit sortOrderChanged(logicalIndex, order); } void LtsDataView::slotRowCountChanged() { if (m_columnFilterProxy && m_columnFilterProxy->sourceModel()) m_ui->lbRows->setText(tr("%1 of %2") .arg(m_columnFilterProxy->rowCount()) .arg(m_columnFilterProxy->sourceModel()->rowCount())); // customview: sometimes the gui is not updated if model is cleared: // (Example: clear JumpPointModel by selecting a workspace WITHOUT JumpPoints) // TODO: needed since custom view rebuild?: if (m_ui->customView && m_columnFilterProxy && m_columnFilterProxy->sourceModel() && m_columnFilterProxy->sourceModel()->rowCount() == 0) m_ui->customView->update(); } void LtsDataView::slotSetHorizontalRange(int min, int max) { m_ui->searchView->horizontalScrollBar()->hide(); // always hide! if (!min && !max) // no min, no max = not visible m_ui->horizontalScrollBar->hide(); else m_ui->horizontalScrollBar->show(); //qWarning() << "hori: min" << min << "max" << max; m_ui->horizontalScrollBar->setRange(min, max); m_ui->horizontalScrollBar->setPageStep(m_ui->searchView->horizontalScrollBar()->pageStep()); } void LtsDataView::updateColumnsVisibility(const QVector &data) { const int colCount = m_columnFilterProxy->columnCount(); for(const auto &ci : data) { if(ci.logicalIndex >= colCount || ci.logicalIndex < 0) continue; m_ui->searchView->setColumnHidden(ci.logicalIndex, ci.hide()); } // save setting saveTableSettings(); } void LtsDataView::slotSetColumnsDefaultOrder() { saveTableSettings(true); m_userChangedColumnPos = false; // save is already done so avoid saving while restoring data restoreTableSettings(); m_userChangedColumnPos = true; } void LtsDataView::slotSetHorizontalValue(int val) { m_ui->horizontalScrollBar->setValue(val); } void LtsDataView::slotSetVerticalRange(int , int ) { // when custom view gets new data (dds data topic), table view and custom view send new ranges, only take the range of the active tab in stacked widget m_ui->tableView->verticalScrollBar()->hide(); // always hide! m_ui->customView->verticalScrollBar()->hide(); // always hide! QScrollBar *whichScrollBar = m_ui->tableView->verticalScrollBar(); if (m_ui->stackedWidget->currentIndex() == CUSTOM) whichScrollBar = m_ui->customView->verticalScrollBar(); int min = whichScrollBar->minimum(); int max = whichScrollBar->maximum(); int pagestep = whichScrollBar->pageStep(); if (!min && !max) // no min, no max = not visible m_ui->verticalScrollBar->hide(); else m_ui->verticalScrollBar->show(); // dont know which scrollbar it is m_ui->verticalScrollBar->setRange(min, max); m_ui->verticalScrollBar->setPageStep(pagestep); } void LtsDataView::slotSetVerticalValue(int val) { QScrollBar *caller = qobject_cast(QObject::sender()); if (caller && caller->isVisible()) m_ui->verticalScrollBar->setVisible(caller->isVisible()); m_ui->verticalScrollBar->setValue(val); } void LtsDataView::slotSectionResized(int index, int oldSize, int newSize) { if (newSize == 0) { m_ui->tableView->setColumnHidden(index, true); return; } if (oldSize == 0) m_ui->tableView->setColumnHidden(index, false); m_ui->tableView->setColumnWidth(index, newSize); } void LtsDataView::slotHeaderMoved(int logicalColumn, int oldColumn, int newColumn) { Q_UNUSED(logicalColumn); m_ui->tableView->header()->moveSection(oldColumn, newColumn); if (m_userChangedColumnPos) { qWarning()<<">>>> [updateColumnsVisibility]"; saveTableSettings(); } } void LtsDataView::slotCsvExport() // TODO: maybe save to dds (office/filemanagement) instead of hd? { if (!m_columnFilterProxy) return; QFileDialog dialog(this); // save dialog //dialog.setFileMode(QFileDialog::ExistingFile); // single file selection dialog.setNameFilter(tr("CSV File (*.csv)")); dialog.setAcceptMode(QFileDialog::AcceptSave); if (!dialog.exec()) return; const QStringList fileNames = dialog.selectedFiles(); if (fileNames.isEmpty()) return; QFile file; file.setFileName(fileNames.first()); if (!file.open(QFile::ReadWrite)) { LtsWidgets::LtsMessageDialog::showMessage(this, tr("File '%1' is not accessible").arg(fileNames.at(0)), tr("CSV Export")); return; } QHeaderView* hdr = m_ui->tableView->header(); QMap colsHeader; // // create header csv const int cols = m_columnFilterProxy->columnCount(); QStringList colsList; colsList.reserve(cols); for (int i = 0; i < cols; ++i) { if (m_ui->stackedWidget->currentIndex() != TABLE || !m_ui->tableView->isColumnHidden(i)) { int visualIndex = hdr->visualIndex(i); colsHeader[visualIndex] = i; //colsList.append(m_columnFilterProxy->headerData(i, Qt::Horizontal).toString()); } } for (const int logicalIndex : qAsConst(colsHeader)) colsList.append(m_columnFilterProxy->headerData(logicalIndex, Qt::Horizontal).toString()); //qCritical() << "slotCsvExport: header colsList = " << colsList; const QString columnCsv = colsList.join(QLatin1Char(';')); file.write(columnCsv.toLocal8Bit()); // write header file.write("\n"); // write csv body //file.write(createCsv().toStdString().c_str()); // create body csv (with all rows) // TODO: threaded? as this can take a while const int rows = m_columnFilterProxy->rowCount(); QStringList rowsList; rowsList.reserve(rows); for (int row = 0; row < rows; ++row) { colsList.clear(); for (const int logicalIndex : qAsConst(colsHeader)) colsList.append(m_columnFilterProxy->data(m_columnFilterProxy->index(row, logicalIndex)).toString()); // for (int col = 0; col < cols; col++) // if (m_ui->stackedWidget->currentIndex() != TABLE || !m_ui->tableView->isColumnHidden(col)) // colsList.append(m_columnFilterProxy->data(m_columnFilterProxy->index(row, col)).toString()); //qCritical() << "slotCsvExport: row = "<< row << ", colsList = " << colsList; rowsList.append(colsList.join(QLatin1Char(';'))); } const QString csvBody = rowsList.join(QLatin1Char('\n')); // TODO: windows? file.write(csvBody.toLocal8Bit()); file.close(); LtsWidgets::LtsMessageDialog::showMessage(this, tr("CSV Export was successful"), tr("CSV Export")); } void LtsDataView::slotResizeColumn(int columnIndex) { m_ui->tableView->resizeColumnToContents(columnIndex); QHeaderView *header = m_ui->searchView->header(); int minimumWidth = header->sectionSize(columnIndex); int contentWidth = m_ui->tableView->columnWidth(columnIndex); if (contentWidth > minimumWidth) header->resizeSection(columnIndex, contentWidth); else m_ui->tableView->header()->resizeSection(columnIndex, minimumWidth); } void LtsDataView::onCustomContextMenuRequested(const QPoint &pos) { if (!m_ui) return; QModelIndex mi; QPoint globalPos; if (m_ui->stackedWidget->currentIndex() == CUSTOM) { if (m_ui->customView) { mi = m_ui->customView->indexAt(pos); globalPos = m_ui->customView->mapToGlobal(pos); } else if (m_ui->tableView) { mi = m_ui->tableView->indexAt(pos); globalPos = m_ui->tableView->mapToGlobal(pos); } } else if (m_ui->stackedWidget->currentIndex() == TABLE) { if (m_ui->tableView) { mi = m_ui->tableView->indexAt(pos); globalPos = m_ui->tableView->mapToGlobal(pos); } } emit signalContextMenuRequested(globalPos, mapToSource(mi)); } //void LtsDataView::setDelegate(std::function delegateFunction) //{ // m_ui->customView->setDelegate(delegateFunction); //} } // namespace LtsDataView