2022-05-26 22:18:54 +01:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
/*
|
|
|
|
* PolyMC - Minecraft Launcher
|
|
|
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
2017-05-22 22:42:47 +01:00
|
|
|
*
|
2022-05-26 22:18:54 +01:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, version 3.
|
2017-05-22 22:42:47 +01:00
|
|
|
*
|
2022-05-26 22:18:54 +01:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2017-05-22 22:42:47 +01:00
|
|
|
*
|
2022-05-26 22:18:54 +01:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* This file incorporates work covered by the following copyright and
|
|
|
|
* permission notice:
|
|
|
|
*
|
|
|
|
* Copyright 2013-2021 MultiMC Contributors
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
2017-05-22 22:42:47 +01:00
|
|
|
*/
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
#include "InstanceView.h"
|
2013-12-24 10:47:30 +00:00
|
|
|
|
2023-07-16 15:46:00 +01:00
|
|
|
#include <QAccessible>
|
2013-12-24 10:47:30 +00:00
|
|
|
#include <QApplication>
|
2023-07-16 15:46:00 +01:00
|
|
|
#include <QCache>
|
2013-12-26 20:16:03 +00:00
|
|
|
#include <QDrag>
|
2023-07-16 15:46:00 +01:00
|
|
|
#include <QFont>
|
|
|
|
#include <QListView>
|
2013-12-26 20:16:03 +00:00
|
|
|
#include <QMimeData>
|
2023-07-16 15:46:00 +01:00
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QPersistentModelIndex>
|
2013-12-26 21:40:26 +00:00
|
|
|
#include <QScrollBar>
|
2023-07-16 15:46:00 +01:00
|
|
|
#include <QtMath>
|
2013-12-26 20:16:03 +00:00
|
|
|
|
2023-07-16 15:46:00 +01:00
|
|
|
#include <QDebug>
|
|
|
|
#include <cstddef>
|
2014-07-12 20:13:23 +01:00
|
|
|
#include "VisualGroup.h"
|
2023-06-07 22:54:32 +01:00
|
|
|
#include "ui/themes/ThemeManager.h"
|
2015-02-02 01:14:14 +00:00
|
|
|
#include <QDebug>
|
2013-12-31 16:26:36 +00:00
|
|
|
|
2021-11-20 15:22:22 +00:00
|
|
|
#include <Application.h>
|
2021-10-25 22:51:42 +01:00
|
|
|
#include <InstanceList.h>
|
|
|
|
|
2023-07-16 15:46:00 +01:00
|
|
|
template <typename T>
|
|
|
|
bool listsIntersect(const QList<T>& l1, const QList<T> t2)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2023-07-16 15:46:00 +01:00
|
|
|
for (auto& item : l1) {
|
|
|
|
if (t2.contains(item)) {
|
2013-12-26 20:16:03 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
InstanceView::InstanceView(QWidget *parent)
|
2014-07-12 20:13:23 +01:00
|
|
|
: QAbstractItemView(parent)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
2013-12-26 20:16:03 +00:00
|
|
|
setAcceptDrops(true);
|
2014-07-12 20:13:23 +01:00
|
|
|
setAutoScroll(true);
|
2023-06-28 16:41:47 +01:00
|
|
|
setPaintCat(APPLICATION->settings()->get("TheCat").toBool());
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
InstanceView::~InstanceView()
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-03 23:40:10 +00:00
|
|
|
qDeleteAll(m_groups);
|
|
|
|
m_groups.clear();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::setModel(QAbstractItemModel *model)
|
2014-03-07 14:46:56 +00:00
|
|
|
{
|
|
|
|
QAbstractItemView::setModel(model);
|
2021-10-25 22:51:42 +01:00
|
|
|
connect(model, &QAbstractItemModel::modelReset, this, &InstanceView::modelReset);
|
|
|
|
connect(model, &QAbstractItemModel::rowsRemoved, this, &InstanceView::rowsRemoved);
|
2014-03-07 14:46:56 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-09 23:51:52 +00:00
|
|
|
scheduleDelayedItemsLayout();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::rowsInserted(const QModelIndex &parent, int start, int end)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-09 23:51:52 +00:00
|
|
|
scheduleDelayedItemsLayout();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-09 23:51:52 +00:00
|
|
|
scheduleDelayedItemsLayout();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::modelReset()
|
2016-12-18 23:34:03 +00:00
|
|
|
{
|
|
|
|
scheduleDelayedItemsLayout();
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::rowsRemoved()
|
2016-12-18 23:34:03 +00:00
|
|
|
{
|
|
|
|
scheduleDelayedItemsLayout();
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
|
2019-07-21 20:12:05 +01:00
|
|
|
{
|
|
|
|
QAbstractItemView::currentChanged(current, previous);
|
2021-10-25 22:51:42 +01:00
|
|
|
// TODO: for accessibility support, implement+register a factory, steal QAccessibleTable from Qt and return an instance of it for InstanceView.
|
2019-10-01 13:28:06 +01:00
|
|
|
#ifndef QT_NO_ACCESSIBILITY
|
2019-07-21 20:12:05 +01:00
|
|
|
if (QAccessible::isActive() && current.isValid()) {
|
|
|
|
QAccessibleEvent event(this, QAccessible::Focus);
|
|
|
|
event.setChild(current.row());
|
|
|
|
QAccessible::updateAccessibility(&event);
|
|
|
|
}
|
2019-10-01 13:28:06 +01:00
|
|
|
#endif /* !QT_NO_ACCESSIBILITY */
|
2019-07-21 20:12:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-10 07:51:24 +00:00
|
|
|
class LocaleString : public QString
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
LocaleString(const char *s) : QString(s)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
LocaleString(const QString &s) : QString(s)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
inline bool operator<(const LocaleString &lhs, const LocaleString &rhs)
|
|
|
|
{
|
|
|
|
return (QString::localeAwareCompare(lhs, rhs) < 0);
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::updateScrollbar()
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2013-12-30 22:10:53 +00:00
|
|
|
int previousScroll = verticalScrollBar()->value();
|
2014-02-03 23:40:10 +00:00
|
|
|
if (m_groups.isEmpty())
|
2013-12-26 21:40:26 +00:00
|
|
|
{
|
|
|
|
verticalScrollBar()->setRange(0, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int totalHeight = 0;
|
2014-02-08 20:46:29 +00:00
|
|
|
// top margin
|
|
|
|
totalHeight += m_categoryMargin;
|
2014-02-09 20:04:00 +00:00
|
|
|
int itemScroll = 0;
|
2014-02-03 23:40:10 +00:00
|
|
|
for (auto category : m_groups)
|
2013-12-26 21:40:26 +00:00
|
|
|
{
|
2014-02-08 20:46:29 +00:00
|
|
|
category->m_verticalPosition = totalHeight;
|
2013-12-26 21:40:26 +00:00
|
|
|
totalHeight += category->totalHeight() + m_categoryMargin;
|
2014-02-09 20:04:00 +00:00
|
|
|
if(!itemScroll && category->totalHeight() != 0)
|
|
|
|
{
|
|
|
|
itemScroll = category->contentHeight() / category->numRows();
|
|
|
|
}
|
2013-12-26 21:40:26 +00:00
|
|
|
}
|
2014-02-09 20:04:00 +00:00
|
|
|
// do not divide by zero
|
|
|
|
if(itemScroll == 0)
|
|
|
|
itemScroll = 64;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-30 17:46:12 +00:00
|
|
|
totalHeight += m_bottomMargin;
|
2014-02-09 19:45:18 +00:00
|
|
|
verticalScrollBar()->setSingleStep ( itemScroll );
|
|
|
|
const int rowsPerPage = qMax ( viewport()->height() / itemScroll, 1 );
|
|
|
|
verticalScrollBar()->setPageStep ( rowsPerPage * itemScroll );
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-01-31 21:51:45 +00:00
|
|
|
verticalScrollBar()->setRange(0, totalHeight - height());
|
2013-12-26 21:40:26 +00:00
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-30 22:10:53 +00:00
|
|
|
verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum()));
|
2018-01-28 01:04:47 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::updateGeometries()
|
2018-01-28 01:04:47 +00:00
|
|
|
{
|
|
|
|
geometryCache.clear();
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2018-01-28 01:04:47 +00:00
|
|
|
QMap<LocaleString, VisualGroup *> cats;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2018-01-28 01:04:47 +00:00
|
|
|
for (int i = 0; i < model()->rowCount(); ++i)
|
|
|
|
{
|
2021-10-25 22:51:42 +01:00
|
|
|
const QString groupName = model()->index(i, 0).data(InstanceViewRoles::GroupRole).toString();
|
2018-01-28 01:04:47 +00:00
|
|
|
if (!cats.contains(groupName))
|
|
|
|
{
|
|
|
|
VisualGroup *old = this->category(groupName);
|
|
|
|
if (old)
|
|
|
|
{
|
|
|
|
auto cat = new VisualGroup(old);
|
|
|
|
cats.insert(groupName, cat);
|
|
|
|
cat->update();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto cat = new VisualGroup(groupName, this);
|
2019-08-20 01:58:27 +01:00
|
|
|
if(fVisibility) {
|
|
|
|
cat->collapsed = fVisibility(groupName);
|
|
|
|
}
|
2018-01-28 01:04:47 +00:00
|
|
|
cats.insert(groupName, cat);
|
|
|
|
cat->update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2018-01-28 01:04:47 +00:00
|
|
|
qDeleteAll(m_groups);
|
|
|
|
m_groups = cats.values();
|
|
|
|
updateScrollbar();
|
2014-02-02 09:26:38 +00:00
|
|
|
viewport()->update();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
bool InstanceView::isIndexHidden(const QModelIndex &index) const
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-07-12 20:13:23 +01:00
|
|
|
VisualGroup *cat = category(index);
|
2013-12-24 10:47:30 +00:00
|
|
|
if (cat)
|
|
|
|
{
|
|
|
|
return cat->collapsed;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
VisualGroup *InstanceView::category(const QModelIndex &index) const
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2021-10-25 22:51:42 +01:00
|
|
|
return category(index.data(InstanceViewRoles::GroupRole).toString());
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
VisualGroup *InstanceView::category(const QString &cat) const
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-03 23:40:10 +00:00
|
|
|
for (auto group : m_groups)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-02 13:27:43 +00:00
|
|
|
if (group->text == cat)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-02 13:27:43 +00:00
|
|
|
return group;
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-02 13:27:43 +00:00
|
|
|
return nullptr;
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
VisualGroup *InstanceView::categoryAt(const QPoint &pos, VisualGroup::HitResults & result) const
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2014-02-03 23:40:10 +00:00
|
|
|
for (auto group : m_groups)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2016-04-11 00:30:50 +01:00
|
|
|
result = group->hitScan(pos);
|
|
|
|
if(result != VisualGroup::NoHit)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2014-02-02 13:27:43 +00:00
|
|
|
return group;
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-11 00:30:50 +01:00
|
|
|
result = VisualGroup::NoHit;
|
2014-02-02 13:27:43 +00:00
|
|
|
return nullptr;
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QString InstanceView::groupNameAt(const QPoint &point)
|
2016-04-11 00:30:50 +01:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2016-04-11 00:30:50 +01:00
|
|
|
VisualGroup::HitResults hitresult;
|
|
|
|
auto group = categoryAt(point + offset(), hitresult);
|
|
|
|
if(group && (hitresult & (VisualGroup::HeaderHit | VisualGroup::BodyHit)))
|
|
|
|
{
|
|
|
|
return group->text;
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
int InstanceView::calculateItemsPerRow() const
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-02-02 13:27:43 +00:00
|
|
|
return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing));
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
int InstanceView::contentWidth() const
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
|
|
|
return width() - m_leftMargin - m_rightMargin;
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
int InstanceView::itemWidth() const
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-07-12 20:13:23 +01:00
|
|
|
return m_itemWidth;
|
2013-12-30 17:46:12 +00:00
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::mousePressEvent(QMouseEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-02-09 23:51:52 +00:00
|
|
|
QPoint visualPos = event->pos();
|
|
|
|
QPoint geometryPos = event->pos() + offset();
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-02-09 23:51:52 +00:00
|
|
|
QPersistentModelIndex index = indexAt(visualPos);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
m_pressedIndex = index;
|
|
|
|
m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex);
|
2014-02-09 23:51:52 +00:00
|
|
|
m_pressedPosition = geometryPos;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2016-04-11 00:30:50 +01:00
|
|
|
VisualGroup::HitResults hitresult;
|
|
|
|
m_pressedCategory = categoryAt(geometryPos, hitresult);
|
|
|
|
if (m_pressedCategory && hitresult & VisualGroup::CheckboxHit)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
|
|
|
setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState);
|
|
|
|
event->accept();
|
|
|
|
return;
|
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (index.isValid() && (index.flags() & Qt::ItemIsEnabled))
|
|
|
|
{
|
2014-07-12 20:13:23 +01:00
|
|
|
if(index != currentIndex())
|
|
|
|
{
|
|
|
|
// FIXME: better!
|
|
|
|
m_currentCursorColumn = -1;
|
|
|
|
}
|
2013-12-26 20:16:03 +00:00
|
|
|
// we disable scrollTo for mouse press so the item doesn't change position
|
|
|
|
// when the user is interacting with it (ie. clicking on it)
|
|
|
|
bool autoScroll = hasAutoScroll();
|
|
|
|
setAutoScroll(false);
|
|
|
|
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
setAutoScroll(autoScroll);
|
2014-07-12 20:13:23 +01:00
|
|
|
QRect rect(visualPos, visualPos);
|
2013-12-30 22:10:53 +00:00
|
|
|
setSelection(rect, QItemSelectionModel::ClearAndSelect);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
// signal handlers may change the model
|
|
|
|
emit pressed(index);
|
2014-01-31 21:51:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-12-26 20:16:03 +00:00
|
|
|
// Forces a finalize() even if mouse is pressed, but not on a item
|
|
|
|
selectionModel()->select(QModelIndex(), QItemSelectionModel::Select);
|
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::mouseMoveEvent(QMouseEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
QPoint topLeft;
|
2014-02-09 23:51:52 +00:00
|
|
|
QPoint visualPos = event->pos();
|
|
|
|
QPoint geometryPos = event->pos() + offset();
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (state() == ExpandingState || state() == CollapsingState)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2013-12-26 20:16:03 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (state() == DraggingState)
|
|
|
|
{
|
2013-12-30 17:46:12 +00:00
|
|
|
topLeft = m_pressedPosition - offset();
|
2013-12-26 20:16:03 +00:00
|
|
|
if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance())
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2013-12-26 20:16:03 +00:00
|
|
|
m_pressedIndex = QModelIndex();
|
|
|
|
startDrag(model()->supportedDragActions());
|
|
|
|
setState(NoState);
|
|
|
|
stopAutoScroll();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2013-12-26 20:16:03 +00:00
|
|
|
return;
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (selectionMode() != SingleSelection)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2013-12-30 17:46:12 +00:00
|
|
|
topLeft = m_pressedPosition - offset();
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-02-09 23:51:52 +00:00
|
|
|
topLeft = geometryPos;
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-01-31 21:51:45 +00:00
|
|
|
if (m_pressedIndex.isValid() && (state() != DragSelectingState) &&
|
|
|
|
(event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty())
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
|
|
|
setState(DraggingState);
|
|
|
|
return;
|
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if ((event->buttons() & Qt::LeftButton) && selectionModel())
|
|
|
|
{
|
|
|
|
setState(DragSelectingState);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-07-12 20:13:23 +01:00
|
|
|
setSelection(QRect(visualPos, visualPos), QItemSelectionModel::ClearAndSelect);
|
2014-02-09 23:51:52 +00:00
|
|
|
QModelIndex index = indexAt(visualPos);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
// set at the end because it might scroll the view
|
2014-01-31 21:51:45 +00:00
|
|
|
if (index.isValid() && (index != selectionModel()->currentIndex()) &&
|
|
|
|
(index.flags() & Qt::ItemIsEnabled))
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
|
|
|
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
|
|
|
|
}
|
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::mouseReleaseEvent(QMouseEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2014-02-09 23:51:52 +00:00
|
|
|
QPoint visualPos = event->pos();
|
|
|
|
QPoint geometryPos = event->pos() + offset();
|
|
|
|
QPersistentModelIndex index = indexAt(visualPos);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2016-04-11 00:30:50 +01:00
|
|
|
VisualGroup::HitResults hitresult;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-01-31 21:51:45 +00:00
|
|
|
bool click = (index == m_pressedIndex && index.isValid()) ||
|
2016-04-11 00:30:50 +01:00
|
|
|
(m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitresult));
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (click && m_pressedCategory)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2013-12-26 20:16:03 +00:00
|
|
|
if (state() == ExpandingState)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2013-12-26 20:16:03 +00:00
|
|
|
m_pressedCategory->collapsed = false;
|
2019-08-20 01:58:27 +01:00
|
|
|
emit groupStateChanged(m_pressedCategory->text, false);
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
updateGeometries();
|
|
|
|
viewport()->update();
|
2013-12-24 10:47:30 +00:00
|
|
|
event->accept();
|
2020-01-08 08:00:54 +00:00
|
|
|
m_pressedCategory = nullptr;
|
|
|
|
setState(NoState);
|
2013-12-24 10:47:30 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-12-26 20:16:03 +00:00
|
|
|
else if (state() == CollapsingState)
|
|
|
|
{
|
|
|
|
m_pressedCategory->collapsed = true;
|
2019-08-20 01:58:27 +01:00
|
|
|
emit groupStateChanged(m_pressedCategory->text, true);
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
updateGeometries();
|
|
|
|
viewport()->update();
|
|
|
|
event->accept();
|
2020-01-08 08:00:54 +00:00
|
|
|
m_pressedCategory = nullptr;
|
|
|
|
setState(NoState);
|
2013-12-26 20:16:03 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
setState(NoState);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (click)
|
|
|
|
{
|
|
|
|
if (event->button() == Qt::LeftButton)
|
|
|
|
{
|
|
|
|
emit clicked(index);
|
|
|
|
}
|
2022-05-02 20:34:55 +01:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
QStyleOptionViewItem option;
|
|
|
|
initViewItemOption(&option);
|
|
|
|
#else
|
2013-12-26 20:16:03 +00:00
|
|
|
QStyleOptionViewItem option = viewOptions();
|
2022-05-02 20:34:55 +01:00
|
|
|
#endif
|
2013-12-26 20:16:03 +00:00
|
|
|
if (m_pressedAlreadySelected)
|
|
|
|
{
|
|
|
|
option.state |= QStyle::State_Selected;
|
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
if ((model()->flags(index) & Qt::ItemIsEnabled) &&
|
|
|
|
style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this))
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
|
|
|
emit activated(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::mouseDoubleClickEvent(QMouseEvent *event)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
QModelIndex index = indexAt(event->pos());
|
2014-01-31 21:51:45 +00:00
|
|
|
if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index))
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2021-10-25 22:51:42 +01:00
|
|
|
QMouseEvent me(
|
|
|
|
QEvent::MouseButtonPress,
|
|
|
|
event->localPos(),
|
|
|
|
event->windowPos(),
|
|
|
|
event->screenPos(),
|
|
|
|
event->button(),
|
|
|
|
event->buttons(),
|
|
|
|
event->modifiers()
|
|
|
|
);
|
2013-12-26 20:16:03 +00:00
|
|
|
mousePressEvent(&me);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// signal handlers may change the model
|
|
|
|
QPersistentModelIndex persistent = index;
|
|
|
|
emit doubleClicked(persistent);
|
2018-11-22 01:02:53 +00:00
|
|
|
|
2022-05-02 20:34:55 +01:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
QStyleOptionViewItem option;
|
|
|
|
initViewItemOption(&option);
|
|
|
|
#else
|
2018-11-22 01:02:53 +00:00
|
|
|
QStyleOptionViewItem option = viewOptions();
|
2022-05-02 20:34:55 +01:00
|
|
|
#endif
|
2018-11-22 01:02:53 +00:00
|
|
|
if ((model()->flags(index) & Qt::ItemIsEnabled) && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this))
|
|
|
|
{
|
|
|
|
emit activated(index);
|
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2023-06-28 16:41:47 +01:00
|
|
|
void InstanceView::setPaintCat(bool visible)
|
2023-06-07 22:54:32 +01:00
|
|
|
{
|
|
|
|
m_catVisible = visible;
|
2023-06-28 16:41:47 +01:00
|
|
|
if (visible)
|
2023-07-03 15:00:56 +01:00
|
|
|
m_catPixmap.load(APPLICATION->getCatPack());
|
2023-06-28 16:41:47 +01:00
|
|
|
else
|
|
|
|
m_catPixmap = QPixmap();
|
2023-06-07 22:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstanceView::paintEvent(QPaintEvent* event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-03-03 18:12:48 +00:00
|
|
|
executeDelayedItemsLayout();
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-24 10:47:30 +00:00
|
|
|
QPainter painter(this->viewport());
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2023-06-07 22:54:32 +01:00
|
|
|
if (m_catVisible) {
|
|
|
|
int widWidth = this->viewport()->width();
|
|
|
|
int widHeight = this->viewport()->height();
|
|
|
|
if (m_catPixmap.width() < widWidth)
|
|
|
|
widWidth = m_catPixmap.width();
|
|
|
|
if (m_catPixmap.height() < widHeight)
|
|
|
|
widHeight = m_catPixmap.height();
|
|
|
|
auto pixmap = m_catPixmap.scaled(widWidth, widHeight, Qt::KeepAspectRatio);
|
|
|
|
QRect rectOfPixmap = pixmap.rect();
|
|
|
|
rectOfPixmap.moveBottomRight(this->viewport()->rect().bottomRight());
|
|
|
|
painter.drawPixmap(rectOfPixmap.topLeft(), pixmap);
|
|
|
|
}
|
|
|
|
|
2022-05-02 20:34:55 +01:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
QStyleOptionViewItem option;
|
|
|
|
initViewItemOption(&option);
|
|
|
|
#else
|
|
|
|
QStyleOptionViewItem option = viewOptions();
|
|
|
|
#endif
|
2014-02-05 00:34:50 +00:00
|
|
|
option.widget = this;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2023-07-16 15:46:00 +01:00
|
|
|
if (model()->rowCount() == 0) {
|
|
|
|
painter.save();
|
2023-07-17 15:22:35 +01:00
|
|
|
const QString line1 = tr("Welcome!");
|
|
|
|
const QString line2 = tr("Add an instance to get started.");
|
2023-07-16 15:46:00 +01:00
|
|
|
auto rect = this->viewport()->rect();
|
|
|
|
auto font = option.font;
|
2023-07-16 19:23:22 +01:00
|
|
|
font.setPointSize(53);
|
2023-07-16 15:46:00 +01:00
|
|
|
painter.setFont(font);
|
|
|
|
auto fm = painter.fontMetrics();
|
|
|
|
|
|
|
|
if (rect.height() <= (fm.height() * 5) || rect.width() <= fm.horizontalAdvance(line2)) {
|
|
|
|
auto s = rect.height() / (5. * fm.height());
|
|
|
|
auto sx = rect.width() * 1. / fm.horizontalAdvance(line2);
|
|
|
|
if (s >= sx)
|
|
|
|
s = sx;
|
|
|
|
auto ps = font.pointSize() * s;
|
|
|
|
if (ps <= 0)
|
|
|
|
ps = 1;
|
|
|
|
font.setPointSize(ps);
|
|
|
|
painter.setFont(font);
|
|
|
|
fm = painter.fontMetrics();
|
|
|
|
}
|
|
|
|
|
|
|
|
// text
|
|
|
|
rect.setTop(rect.top() + fm.height() * 1.5);
|
|
|
|
painter.drawText(rect, Qt::AlignHCenter, line1);
|
|
|
|
rect.setTop(rect.top() + fm.height());
|
|
|
|
painter.drawText(rect, Qt::AlignHCenter, line2);
|
|
|
|
painter.restore();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-08 20:46:29 +00:00
|
|
|
int wpWidth = viewport()->width();
|
|
|
|
option.rect.setWidth(wpWidth);
|
2023-07-16 15:46:00 +01:00
|
|
|
for (int i = 0; i < m_groups.size(); ++i) {
|
|
|
|
VisualGroup* category = m_groups.at(i);
|
2014-02-08 20:46:29 +00:00
|
|
|
int y = category->verticalPosition();
|
|
|
|
y -= verticalOffset();
|
|
|
|
QRect backup = option.rect;
|
|
|
|
int height = category->totalHeight();
|
|
|
|
option.rect.setTop(y);
|
|
|
|
option.rect.setHeight(height);
|
|
|
|
option.rect.setLeft(m_leftMargin);
|
|
|
|
option.rect.setRight(wpWidth - m_rightMargin);
|
|
|
|
category->drawHeader(&painter, option);
|
2013-12-24 10:47:30 +00:00
|
|
|
y += category->totalHeight() + m_categoryMargin;
|
2014-02-08 20:46:29 +00:00
|
|
|
option.rect = backup;
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-24 10:47:30 +00:00
|
|
|
for (int i = 0; i < model()->rowCount(); ++i)
|
|
|
|
{
|
|
|
|
const QModelIndex index = model()->index(i, 0);
|
|
|
|
if (isIndexHidden(index))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Qt::ItemFlags flags = index.flags();
|
|
|
|
option.rect = visualRect(index);
|
2017-05-21 19:20:37 +01:00
|
|
|
option.features |= QStyleOptionViewItem::WrapText;
|
2013-12-30 22:10:53 +00:00
|
|
|
if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index))
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-01-31 21:51:45 +00:00
|
|
|
option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected
|
|
|
|
: QStyle::State_None;
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
option.state &= ~QStyle::State_Selected;
|
|
|
|
}
|
|
|
|
option.state |= (index == currentIndex()) ? QStyle::State_HasFocus : QStyle::State_None;
|
|
|
|
if (!(flags & Qt::ItemIsEnabled))
|
|
|
|
{
|
|
|
|
option.state &= ~QStyle::State_Enabled;
|
|
|
|
}
|
|
|
|
itemDelegate()->paint(&painter, option, index);
|
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-02-03 23:40:10 +00:00
|
|
|
/*
|
|
|
|
* Drop indicators for manual reordering...
|
|
|
|
*/
|
|
|
|
#if 0
|
2013-12-26 20:16:03 +00:00
|
|
|
if (!m_lastDragPosition.isNull())
|
|
|
|
{
|
2022-05-02 20:34:55 +01:00
|
|
|
std::pair<VisualGroup *, VisualGroup::HitResults> pair = rowDropPos(m_lastDragPosition);
|
|
|
|
VisualGroup *category = pair.first;
|
|
|
|
VisualGroup::HitResults row = pair.second;
|
2013-12-26 20:16:03 +00:00
|
|
|
if (category)
|
|
|
|
{
|
2014-02-03 23:40:10 +00:00
|
|
|
int internalRow = row - category->firstItemIndex;
|
2013-12-26 20:16:03 +00:00
|
|
|
QLine line;
|
2013-12-31 16:26:36 +00:00
|
|
|
if (internalRow >= category->numItems())
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2013-12-31 16:26:36 +00:00
|
|
|
QRect toTheRightOfRect = visualRect(category->lastItem());
|
2013-12-26 20:16:03 +00:00
|
|
|
line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QRect toTheLeftOfRect = visualRect(model()->index(row, 0));
|
|
|
|
line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft());
|
|
|
|
}
|
|
|
|
painter.save();
|
|
|
|
painter.setPen(QPen(Qt::black, 3));
|
|
|
|
painter.drawLine(line);
|
|
|
|
painter.restore();
|
|
|
|
}
|
|
|
|
}
|
2014-02-03 23:40:10 +00:00
|
|
|
#endif
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::resizeEvent(QResizeEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-07-12 20:13:23 +01:00
|
|
|
int newItemsPerRow = calculateItemsPerRow();
|
|
|
|
if(newItemsPerRow != m_currentItemsPerRow)
|
|
|
|
{
|
2014-07-12 22:27:32 +01:00
|
|
|
m_currentCursorColumn = -1;
|
2014-07-12 20:13:23 +01:00
|
|
|
m_currentItemsPerRow = newItemsPerRow;
|
|
|
|
updateGeometries();
|
|
|
|
}
|
2018-01-28 01:04:47 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
updateScrollbar();
|
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::dragEnterEvent(QDragEnterEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (!isDragEventAccepted(event))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2013-12-30 17:46:12 +00:00
|
|
|
m_lastDragPosition = event->pos() + offset();
|
2013-12-26 20:16:03 +00:00
|
|
|
viewport()->update();
|
|
|
|
event->accept();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::dragMoveEvent(QDragMoveEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
if (!isDragEventAccepted(event))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2013-12-30 17:46:12 +00:00
|
|
|
m_lastDragPosition = event->pos() + offset();
|
2013-12-26 20:16:03 +00:00
|
|
|
viewport()->update();
|
|
|
|
event->accept();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::dragLeaveEvent(QDragLeaveEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
m_lastDragPosition = QPoint();
|
|
|
|
viewport()->update();
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::dropEvent(QDropEvent *event)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
m_lastDragPosition = QPoint();
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2013-12-24 10:47:30 +00:00
|
|
|
stopAutoScroll();
|
|
|
|
setState(NoState);
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
auto mimedata = event->mimeData();
|
|
|
|
|
2017-04-22 05:11:26 +01:00
|
|
|
if (event->source() == this)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2017-04-22 05:11:26 +01:00
|
|
|
if(event->possibleActions() & Qt::MoveAction)
|
|
|
|
{
|
2022-05-02 20:34:55 +01:00
|
|
|
std::pair<VisualGroup *, VisualGroup::HitResults> dropPos = rowDropPos(event->pos());
|
2021-10-25 22:51:42 +01:00
|
|
|
const VisualGroup *group = dropPos.first;
|
|
|
|
auto hitresult = dropPos.second;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
if (hitresult == VisualGroup::HitResult::NoHit)
|
2017-04-22 05:11:26 +01:00
|
|
|
{
|
|
|
|
viewport()->update();
|
|
|
|
return;
|
|
|
|
}
|
2021-10-25 22:51:42 +01:00
|
|
|
auto instanceId = QString::fromUtf8(mimedata->data("application/x-instanceid"));
|
2021-11-20 15:22:22 +00:00
|
|
|
auto instanceList = APPLICATION->instances().get();
|
2021-10-25 22:51:42 +01:00
|
|
|
instanceList->setInstanceGroup(instanceId, group->text);
|
|
|
|
event->setDropAction(Qt::MoveAction);
|
|
|
|
event->accept();
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2017-04-22 05:11:26 +01:00
|
|
|
updateGeometries();
|
|
|
|
viewport()->update();
|
|
|
|
}
|
2021-10-25 22:51:42 +01:00
|
|
|
return;
|
2017-04-22 05:11:26 +01:00
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2017-04-22 05:11:26 +01:00
|
|
|
// check if the action is supported
|
|
|
|
if (!mimedata)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2013-12-26 20:16:03 +00:00
|
|
|
return;
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2017-04-22 05:11:26 +01:00
|
|
|
// files dropped from outside?
|
|
|
|
if (mimedata->hasUrls())
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2017-04-22 05:11:26 +01:00
|
|
|
auto urls = mimedata->urls();
|
2017-04-23 15:50:48 +01:00
|
|
|
event->accept();
|
2017-04-22 05:11:26 +01:00
|
|
|
emit droppedURLs(urls);
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::startDrag(Qt::DropActions supportedActions)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
QModelIndexList indexes = selectionModel()->selectedIndexes();
|
2014-07-12 20:13:23 +01:00
|
|
|
if(indexes.count() == 0)
|
|
|
|
return;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-07-12 20:13:23 +01:00
|
|
|
QMimeData *data = model()->mimeData(indexes);
|
|
|
|
if (!data)
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2014-07-12 20:13:23 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
QRect rect;
|
|
|
|
QPixmap pixmap = renderToPixmap(indexes, &rect);
|
|
|
|
QDrag *drag = new QDrag(this);
|
|
|
|
drag->setPixmap(pixmap);
|
|
|
|
drag->setMimeData(data);
|
2021-10-27 09:25:18 +01:00
|
|
|
drag->setHotSpot(m_pressedPosition - rect.topLeft());
|
2014-07-12 20:13:23 +01:00
|
|
|
Qt::DropAction defaultDropAction = Qt::IgnoreAction;
|
2021-10-25 22:51:42 +01:00
|
|
|
if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction()))
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
|
|
|
defaultDropAction = this->defaultDropAction();
|
|
|
|
}
|
2021-10-25 22:51:42 +01:00
|
|
|
/*auto action = */
|
|
|
|
drag->exec(supportedActions, defaultDropAction);
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QRect InstanceView::visualRect(const QModelIndex &index) const
|
2014-02-02 13:27:43 +00:00
|
|
|
{
|
2021-10-25 22:51:42 +01:00
|
|
|
const_cast<InstanceView*>(this)->executeDelayedItemsLayout();
|
2019-07-02 01:09:41 +01:00
|
|
|
|
2014-02-02 13:27:43 +00:00
|
|
|
return geometryRect(index).translated(-offset());
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QRect InstanceView::geometryRect(const QModelIndex &index) const
|
2013-12-24 10:47:30 +00:00
|
|
|
{
|
2021-10-25 22:51:42 +01:00
|
|
|
const_cast<InstanceView*>(this)->executeDelayedItemsLayout();
|
2019-07-02 01:09:41 +01:00
|
|
|
|
2013-12-24 10:47:30 +00:00
|
|
|
if (!index.isValid() || isIndexHidden(index) || index.column() > 0)
|
|
|
|
{
|
|
|
|
return QRect();
|
|
|
|
}
|
|
|
|
|
2014-07-12 11:49:07 +01:00
|
|
|
int row = index.row();
|
|
|
|
if(geometryCache.contains(row))
|
|
|
|
{
|
|
|
|
return *geometryCache[row];
|
|
|
|
}
|
2013-12-24 10:47:30 +00:00
|
|
|
|
2014-07-12 20:13:23 +01:00
|
|
|
const VisualGroup *cat = category(index);
|
|
|
|
QPair<int, int> pos = cat->positionOf(index);
|
|
|
|
int x = pos.first;
|
|
|
|
// int y = pos.second;
|
2013-12-24 10:47:30 +00:00
|
|
|
|
2022-05-02 20:34:55 +01:00
|
|
|
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
QStyleOptionViewItem option;
|
|
|
|
initViewItemOption(&option);
|
|
|
|
#else
|
|
|
|
QStyleOptionViewItem option = viewOptions();
|
|
|
|
#endif
|
|
|
|
|
2014-07-12 20:13:23 +01:00
|
|
|
QRect out;
|
|
|
|
out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + cat->rowTopOf(index));
|
|
|
|
out.setLeft(m_spacing + x * (itemWidth() + m_spacing));
|
2022-05-02 20:34:55 +01:00
|
|
|
out.setSize(itemDelegate()->sizeHint(option, index));
|
2014-07-12 20:13:23 +01:00
|
|
|
geometryCache.insert(row, new QRect(out));
|
|
|
|
return out;
|
2013-12-24 10:47:30 +00:00
|
|
|
}
|
2013-12-26 20:16:03 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QModelIndex InstanceView::indexAt(const QPoint &point) const
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2021-10-25 22:51:42 +01:00
|
|
|
const_cast<InstanceView*>(this)->executeDelayedItemsLayout();
|
2016-10-26 17:12:33 +01:00
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
for (int i = 0; i < model()->rowCount(); ++i)
|
|
|
|
{
|
|
|
|
QModelIndex index = model()->index(i, 0);
|
2014-02-09 23:51:52 +00:00
|
|
|
if (visualRect(index).contains(point))
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2019-07-02 01:09:41 +01:00
|
|
|
executeDelayedItemsLayout();
|
|
|
|
|
2013-12-26 20:16:03 +00:00
|
|
|
for (int i = 0; i < model()->rowCount(); ++i)
|
|
|
|
{
|
|
|
|
QModelIndex index = model()->index(i, 0);
|
2014-07-12 20:13:23 +01:00
|
|
|
QRect itemRect = visualRect(index);
|
2014-02-02 15:57:00 +00:00
|
|
|
if (itemRect.intersects(rect))
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2013-12-30 22:10:53 +00:00
|
|
|
selectionModel()->select(index, commands);
|
2014-02-02 15:57:00 +00:00
|
|
|
update(itemRect.translated(-offset()));
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QPixmap InstanceView::renderToPixmap(const QModelIndexList &indices, QRect *r) const
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(r);
|
2014-01-31 21:51:45 +00:00
|
|
|
auto paintPairs = draggablePaintPairs(indices, r);
|
2013-12-26 20:16:03 +00:00
|
|
|
if (paintPairs.isEmpty())
|
|
|
|
{
|
|
|
|
return QPixmap();
|
|
|
|
}
|
|
|
|
QPixmap pixmap(r->size());
|
|
|
|
pixmap.fill(Qt::transparent);
|
|
|
|
QPainter painter(&pixmap);
|
2022-05-02 20:34:55 +01:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
|
|
QStyleOptionViewItem option;
|
|
|
|
initViewItemOption(&option);
|
|
|
|
#else
|
2013-12-26 20:16:03 +00:00
|
|
|
QStyleOptionViewItem option = viewOptions();
|
2022-05-02 20:34:55 +01:00
|
|
|
#endif
|
2013-12-26 20:16:03 +00:00
|
|
|
option.state |= QStyle::State_Selected;
|
|
|
|
for (int j = 0; j < paintPairs.count(); ++j)
|
|
|
|
{
|
|
|
|
option.rect = paintPairs.at(j).first.translated(-r->topLeft());
|
|
|
|
const QModelIndex ¤t = paintPairs.at(j).second;
|
|
|
|
itemDelegate()->paint(&painter, option, current);
|
|
|
|
}
|
|
|
|
return pixmap;
|
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2022-05-02 20:34:55 +01:00
|
|
|
QList<std::pair<QRect, QModelIndex>> InstanceView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(r);
|
|
|
|
QRect &rect = *r;
|
2022-05-02 20:34:55 +01:00
|
|
|
QList<std::pair<QRect, QModelIndex>> ret;
|
2014-01-31 21:51:45 +00:00
|
|
|
for (int i = 0; i < indices.count(); ++i)
|
|
|
|
{
|
2013-12-26 20:16:03 +00:00
|
|
|
const QModelIndex &index = indices.at(i);
|
2014-02-02 13:27:43 +00:00
|
|
|
const QRect current = geometryRect(index);
|
2022-05-02 20:34:55 +01:00
|
|
|
ret += std::make_pair(current, index);
|
2014-02-02 17:30:52 +00:00
|
|
|
rect |= current;
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
bool InstanceView::isDragEventAccepted(QDropEvent *event)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2017-04-22 05:11:26 +01:00
|
|
|
return true;
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
2014-01-31 21:51:45 +00:00
|
|
|
|
2022-05-02 20:34:55 +01:00
|
|
|
std::pair<VisualGroup *, VisualGroup::HitResults> InstanceView::rowDropPos(const QPoint &pos)
|
2013-12-26 20:16:03 +00:00
|
|
|
{
|
2021-10-25 22:51:42 +01:00
|
|
|
VisualGroup::HitResults hitresult;
|
|
|
|
auto group = categoryAt(pos + offset(), hitresult);
|
2022-05-02 20:34:55 +01:00
|
|
|
return std::make_pair(group, hitresult);
|
2013-12-26 20:16:03 +00:00
|
|
|
}
|
2013-12-30 17:46:12 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QPoint InstanceView::offset() const
|
2013-12-30 17:46:12 +00:00
|
|
|
{
|
|
|
|
return QPoint(horizontalOffset(), verticalOffset());
|
|
|
|
}
|
2014-02-02 13:27:43 +00:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QRegion InstanceView::visualRegionForSelection(const QItemSelection &selection) const
|
2014-02-02 13:27:43 +00:00
|
|
|
{
|
|
|
|
QRegion region;
|
|
|
|
for (auto &range : selection)
|
|
|
|
{
|
|
|
|
int start_row = range.top();
|
|
|
|
int end_row = range.bottom();
|
|
|
|
for (int row = start_row; row <= end_row; ++row)
|
|
|
|
{
|
|
|
|
int start_column = range.left();
|
|
|
|
int end_column = range.right();
|
|
|
|
for (int column = start_column; column <= end_column; ++column)
|
|
|
|
{
|
|
|
|
QModelIndex index = model()->index(row, column, rootIndex());
|
|
|
|
region += visualRect(index); // OK
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return region;
|
|
|
|
}
|
2014-07-12 20:13:23 +01:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
|
2014-02-02 17:30:52 +00:00
|
|
|
{
|
|
|
|
auto current = currentIndex();
|
|
|
|
if(!current.isValid())
|
|
|
|
{
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
auto cat = category(current);
|
2014-07-12 20:13:23 +01:00
|
|
|
int group_index = m_groups.indexOf(cat);
|
|
|
|
if(group_index < 0)
|
|
|
|
return current;
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-07-12 20:13:23 +01:00
|
|
|
QPair<int, int> pos = cat->positionOf(current);
|
|
|
|
int column = pos.first;
|
|
|
|
int row = pos.second;
|
|
|
|
if(m_currentCursorColumn < 0)
|
2014-02-02 17:30:52 +00:00
|
|
|
{
|
2014-07-12 20:13:23 +01:00
|
|
|
m_currentCursorColumn = column;
|
|
|
|
}
|
|
|
|
switch(cursorAction)
|
|
|
|
{
|
|
|
|
case MoveUp:
|
2014-02-02 17:30:52 +00:00
|
|
|
{
|
2014-07-12 20:13:23 +01:00
|
|
|
if(row == 0)
|
|
|
|
{
|
2014-07-12 22:27:32 +01:00
|
|
|
int prevgroupindex = group_index-1;
|
|
|
|
while(prevgroupindex >= 0)
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
2014-07-12 22:27:32 +01:00
|
|
|
auto prevgroup = m_groups[prevgroupindex];
|
|
|
|
if(prevgroup->collapsed)
|
|
|
|
{
|
|
|
|
prevgroupindex--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int newRow = prevgroup->numRows() - 1;
|
|
|
|
int newRowSize = prevgroup->rows[newRow].size();
|
|
|
|
int newColumn = m_currentCursorColumn;
|
|
|
|
if (m_currentCursorColumn >= newRowSize)
|
|
|
|
{
|
|
|
|
newColumn = newRowSize - 1;
|
|
|
|
}
|
|
|
|
return prevgroup->rows[newRow][newColumn];
|
2014-07-12 20:13:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int newRow = row - 1;
|
|
|
|
int newRowSize = cat->rows[newRow].size();
|
|
|
|
int newColumn = m_currentCursorColumn;
|
|
|
|
if (m_currentCursorColumn >= newRowSize)
|
|
|
|
{
|
|
|
|
newColumn = newRowSize - 1;
|
|
|
|
}
|
|
|
|
return cat->rows[newRow][newColumn];
|
|
|
|
}
|
2014-07-12 22:27:32 +01:00
|
|
|
return current;
|
2014-02-02 17:30:52 +00:00
|
|
|
}
|
2014-07-12 20:13:23 +01:00
|
|
|
case MoveDown:
|
|
|
|
{
|
|
|
|
if(row == cat->rows.size() - 1)
|
|
|
|
{
|
2014-07-12 22:27:32 +01:00
|
|
|
int nextgroupindex = group_index+1;
|
|
|
|
while (nextgroupindex < m_groups.size())
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
2014-07-12 22:27:32 +01:00
|
|
|
auto nextgroup = m_groups[nextgroupindex];
|
|
|
|
if(nextgroup->collapsed)
|
|
|
|
{
|
|
|
|
nextgroupindex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int newRowSize = nextgroup->rows[0].size();
|
|
|
|
int newColumn = m_currentCursorColumn;
|
|
|
|
if (m_currentCursorColumn >= newRowSize)
|
|
|
|
{
|
|
|
|
newColumn = newRowSize - 1;
|
|
|
|
}
|
|
|
|
return nextgroup->rows[0][newColumn];
|
2014-07-12 20:13:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int newRow = row + 1;
|
|
|
|
int newRowSize = cat->rows[newRow].size();
|
|
|
|
int newColumn = m_currentCursorColumn;
|
|
|
|
if (m_currentCursorColumn >= newRowSize)
|
|
|
|
{
|
|
|
|
newColumn = newRowSize - 1;
|
|
|
|
}
|
|
|
|
return cat->rows[newRow][newColumn];
|
|
|
|
}
|
2014-07-12 22:27:32 +01:00
|
|
|
return current;
|
2014-07-12 20:13:23 +01:00
|
|
|
}
|
|
|
|
case MoveLeft:
|
|
|
|
{
|
|
|
|
if(column > 0)
|
|
|
|
{
|
|
|
|
m_currentCursorColumn = column - 1;
|
|
|
|
return cat->rows[row][column - 1];
|
|
|
|
}
|
2014-07-12 22:27:32 +01:00
|
|
|
// TODO: moving to previous line
|
2014-07-12 20:13:23 +01:00
|
|
|
return current;
|
|
|
|
}
|
|
|
|
case MoveRight:
|
|
|
|
{
|
|
|
|
if(column < cat->rows[row].size() - 1)
|
|
|
|
{
|
|
|
|
m_currentCursorColumn = column + 1;
|
|
|
|
return cat->rows[row][column + 1];
|
|
|
|
}
|
2014-07-12 22:27:32 +01:00
|
|
|
// TODO: moving to next line
|
2014-07-12 20:13:23 +01:00
|
|
|
return current;
|
|
|
|
}
|
2014-07-12 22:27:32 +01:00
|
|
|
case MoveHome:
|
|
|
|
{
|
|
|
|
m_currentCursorColumn = 0;
|
|
|
|
return cat->rows[row][0];
|
|
|
|
}
|
|
|
|
case MoveEnd:
|
|
|
|
{
|
|
|
|
auto last = cat->rows[row].size() - 1;
|
|
|
|
m_currentCursorColumn = last;
|
|
|
|
return cat->rows[row][last];
|
|
|
|
}
|
2014-07-12 20:13:23 +01:00
|
|
|
default:
|
|
|
|
break;
|
2014-02-02 17:30:52 +00:00
|
|
|
}
|
|
|
|
return current;
|
|
|
|
}
|
2014-07-12 20:13:23 +01:00
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
int InstanceView::horizontalOffset() const
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
|
|
|
return horizontalScrollBar()->value();
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
int InstanceView::verticalOffset() const
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
|
|
|
return verticalScrollBar()->value();
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::scrollContentsBy(int dx, int dy)
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
|
|
|
scrollDirtyRegion(dx, dy);
|
|
|
|
viewport()->scroll(dx, dy);
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
void InstanceView::scrollTo(const QModelIndex &index, ScrollHint hint)
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
|
|
|
if (!index.isValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
const QRect rect = visualRect(index);
|
|
|
|
if (hint == EnsureVisible && viewport()->rect().contains(rect))
|
|
|
|
{
|
|
|
|
viewport()->update(rect);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
verticalScrollBar()->setValue(verticalScrollToValue(index, rect, hint));
|
|
|
|
}
|
|
|
|
|
2021-10-25 22:51:42 +01:00
|
|
|
int InstanceView::verticalScrollToValue(const QModelIndex &index, const QRect &rect, QListView::ScrollHint hint) const
|
2014-07-12 20:13:23 +01:00
|
|
|
{
|
|
|
|
const QRect area = viewport()->rect();
|
|
|
|
const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top());
|
|
|
|
const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom());
|
2018-07-15 13:51:05 +01:00
|
|
|
|
2014-07-12 20:13:23 +01:00
|
|
|
int verticalValue = verticalScrollBar()->value();
|
|
|
|
QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing());
|
|
|
|
if (hint == QListView::PositionAtTop || above)
|
|
|
|
verticalValue += adjusted.top();
|
|
|
|
else if (hint == QListView::PositionAtBottom || below)
|
|
|
|
verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1);
|
|
|
|
else if (hint == QListView::PositionAtCenter)
|
|
|
|
verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2);
|
|
|
|
return verticalValue;
|
|
|
|
}
|