262 lines
9.0 KiB
C++
262 lines
9.0 KiB
C++
/*
|
|
* Cantata
|
|
*
|
|
* Copyright (c) 2011-2022 Craig Drummond <craig.p.drummond@gmail.com>
|
|
*
|
|
* ----
|
|
*
|
|
* 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; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "tableview.h"
|
|
#include "stretchheaderview.h"
|
|
#include "basicitemdelegate.h"
|
|
#include "ratingwidget.h"
|
|
#include "support/configuration.h"
|
|
#include "models/roles.h"
|
|
#include "mpd-interface/song.h"
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
#include <QActionGroup>
|
|
#include <QPainter>
|
|
#include <QStyleOption>
|
|
|
|
class TableViewItemDelegate : public BasicItemDelegate
|
|
{
|
|
public:
|
|
TableViewItemDelegate(QObject *p, int rc) : BasicItemDelegate(p), ratingCol(rc), ratingPainter(nullptr) { }
|
|
~TableViewItemDelegate() override { delete ratingPainter; }
|
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
|
|
{
|
|
if (!index.isValid()) {
|
|
return;
|
|
}
|
|
|
|
QStyleOptionViewItem v4((QStyleOptionViewItem &)option);
|
|
if (QStyleOptionViewItem::Beginning==v4.viewItemPosition) {
|
|
v4.icon=index.data(Cantata::Role_Decoration).value<QIcon>();
|
|
if (!v4.icon.isNull()) {
|
|
v4.features |= QStyleOptionViewItem::HasDecoration;
|
|
v4.decorationSize=v4.icon.actualSize(option.decorationSize, QIcon::Normal, QIcon::Off);
|
|
}
|
|
}
|
|
|
|
BasicItemDelegate::paint(painter, v4, index);
|
|
|
|
if (index.column()==ratingCol) {
|
|
Song song=index.data(Cantata::Role_SongWithRating).value<Song>();
|
|
if (song.rating>0 && song.rating<=Song::Rating_Max) {
|
|
const QRect &r=option.rect;
|
|
if (!ratingPainter) {
|
|
ratingPainter=new RatingPainter(r.height()-4);
|
|
ratingPainter->setColor(option.palette.color(QPalette::Active, QPalette::Text));
|
|
}
|
|
painter->save();
|
|
painter->setOpacity(painter->opacity()*0.75);
|
|
painter->setClipRect(r);
|
|
const QSize &ratingSize=ratingPainter->size();
|
|
QRect ratingRect(r.x()+(r.width()-ratingSize.width())/2, r.y()+(r.height()-ratingSize.height())/2,
|
|
ratingSize.width(), ratingSize.height());
|
|
ratingPainter->paint(painter, ratingRect, song.rating);
|
|
painter->restore();
|
|
}
|
|
}
|
|
}
|
|
|
|
int ratingCol;
|
|
mutable RatingPainter *ratingPainter;
|
|
};
|
|
|
|
TableView::TableView(const QString &cfgGroup, QWidget *parent, bool menuAlwaysAllowed)
|
|
: TreeView(parent, menuAlwaysAllowed)
|
|
, menu(nullptr)
|
|
, configGroup(cfgGroup)
|
|
, menuIsForCol(-1)
|
|
{
|
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
|
setAcceptDrops(true);
|
|
setDragDropOverwriteMode(false);
|
|
setDragDropMode(QAbstractItemView::DragDrop);
|
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
setDropIndicatorShown(true);
|
|
setUniformRowHeights(true);
|
|
setAttribute(Qt::WA_MouseTracking, true);
|
|
StretchHeaderView *hdr=new StretchHeaderView(Qt::Horizontal, this);
|
|
setHeader(hdr);
|
|
connect(hdr, SIGNAL(StretchEnabledChanged(bool)), SLOT(stretchToggled(bool)));
|
|
}
|
|
|
|
void TableView::setModel(QAbstractItemModel *m)
|
|
{
|
|
if (dynamic_cast<TableViewItemDelegate *>(itemDelegate())) {
|
|
itemDelegate()->deleteLater();
|
|
}
|
|
bool ok=false;
|
|
int col=m ? m->data(QModelIndex(), Cantata::Role_RatingCol).toInt(&ok) : -1;
|
|
if (ok && col>=0) {
|
|
setItemDelegate(new TableViewItemDelegate(this, col));
|
|
}
|
|
TreeView::setModel(m);
|
|
}
|
|
|
|
static const QLatin1String constHeaderKey("tableHeader");
|
|
|
|
void TableView::initHeader()
|
|
{
|
|
if (!model()) {
|
|
return;
|
|
}
|
|
|
|
StretchHeaderView *hdr=qobject_cast<StretchHeaderView *>(header());
|
|
QList<int> hideable;
|
|
QList<int> initiallyHidden;
|
|
if (!menu) {
|
|
hdr->SetStretchEnabled(true);
|
|
stretchToggled(true);
|
|
hdr->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
for (int i=0; i<model()->columnCount(); ++i) {
|
|
hdr->SetColumnWidth(i, model()->headerData(i, Qt::Horizontal, Cantata::Role_Width).toDouble());
|
|
if (model()->headerData(i, Qt::Horizontal, Cantata::Role_Hideable).toBool()) {
|
|
hideable.append(i);
|
|
}
|
|
if (model()->headerData(i, Qt::Horizontal, Cantata::Role_InitiallyHidden).toBool()) {
|
|
initiallyHidden.append(i);
|
|
}
|
|
}
|
|
hdr->setSectionsMovable(true);
|
|
connect(hdr, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
|
|
}
|
|
|
|
//Restore state
|
|
Configuration config(configGroup);
|
|
QByteArray state=config.get(constHeaderKey, QByteArray());
|
|
if (state.isEmpty()) {
|
|
for (int i: initiallyHidden) {
|
|
hdr->HideSection(i);
|
|
}
|
|
} else {
|
|
hdr->RestoreState(state);
|
|
}
|
|
|
|
if (!menu) {
|
|
menu = new QMenu(this);
|
|
QAction *stretch=new QAction(tr("Stretch Columns To Fit Window"), this);
|
|
stretch->setCheckable(true);
|
|
stretch->setChecked(hdr->is_stretch_enabled());
|
|
connect(stretch, SIGNAL(toggled(bool)), hdr, SLOT(SetStretchEnabled(bool)));
|
|
menu->addAction(stretch);
|
|
|
|
QMenu *alignmentMenu = new QMenu(menu);
|
|
QActionGroup *alignGroup = new QActionGroup(alignmentMenu);
|
|
alignLeftAction = new QAction(tr("Left"), alignGroup);
|
|
alignCenterAction = new QAction(tr("Center"), alignGroup);
|
|
alignRightAction = new QAction(tr("Right"), alignGroup);
|
|
|
|
alignLeftAction->setCheckable(true);
|
|
alignCenterAction->setCheckable(true);
|
|
alignRightAction->setCheckable(true);
|
|
alignmentMenu->addActions(alignGroup->actions());
|
|
connect(alignGroup, SIGNAL(triggered(QAction*)), SLOT(alignmentChanged()));
|
|
alignAction=new QAction(tr("Alignment"), menu);
|
|
alignAction->setMenu(alignmentMenu);
|
|
menu->addAction(alignAction);
|
|
|
|
menu->addSeparator();
|
|
for (int col: hideable) {
|
|
QAction *act=new QAction(model()->headerData(col, Qt::Horizontal, Cantata::Role_ContextMenuText).toString(), menu);
|
|
act->setCheckable(true);
|
|
act->setChecked(!hdr->isSectionHidden(col));
|
|
menu->addAction(act);
|
|
act->setData(col);
|
|
connect(act, SIGNAL(toggled(bool)), this, SLOT(toggleHeaderItem(bool)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void TableView::saveHeader()
|
|
{
|
|
if (menu && model()) {
|
|
Configuration(configGroup).set(constHeaderKey, qobject_cast<StretchHeaderView *>(header())->SaveState());
|
|
}
|
|
}
|
|
|
|
void TableView::scrollTo(const QModelIndex &index, ScrollHint hint)
|
|
{
|
|
QHeaderView *hdr=header();
|
|
|
|
// scrollTo does not work if column hidden, so find first one that is not.
|
|
if (hdr && hdr->isSectionHidden(index.column())) {
|
|
for (int i=0; i<model()->columnCount(); ++i) {
|
|
if (!hdr->isSectionHidden(i)) {
|
|
TreeView::scrollTo(model()->index(index.row(), i, index.parent()), hint);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
TreeView::scrollTo(index, hint);
|
|
}
|
|
|
|
void TableView::showMenu(const QPoint &pos)
|
|
{
|
|
menuIsForCol=header()->logicalIndexAt(pos);
|
|
alignAction->setEnabled(-1!=menuIsForCol);
|
|
if (-1!=menuIsForCol) {
|
|
Qt::Alignment al = (Qt::AlignmentFlag)model()->headerData(menuIsForCol, Qt::Horizontal, Qt::TextAlignmentRole).toInt();
|
|
if (al&Qt::AlignLeft) {
|
|
alignLeftAction->setChecked(true);
|
|
} else if (al&Qt::AlignHCenter) {
|
|
alignCenterAction->setChecked(true);
|
|
} else if (al&Qt::AlignRight) {
|
|
alignRightAction->setChecked(true);
|
|
}
|
|
}
|
|
menu->exec(mapToGlobal(pos));
|
|
}
|
|
|
|
void TableView::toggleHeaderItem(bool visible)
|
|
{
|
|
QAction *act=qobject_cast<QAction *>(sender());
|
|
|
|
if (act) {
|
|
int index=act->data().toInt();
|
|
if (-1!=index) {
|
|
qobject_cast<StretchHeaderView *>(header())->SetSectionHidden(index, !visible);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TableView::stretchToggled(bool e)
|
|
{
|
|
setHorizontalScrollBarPolicy(e ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAsNeeded);
|
|
}
|
|
|
|
void TableView::alignmentChanged()
|
|
{
|
|
if (-1!=menuIsForCol) {
|
|
int al=alignLeftAction->isChecked()
|
|
? Qt::AlignLeft
|
|
: alignRightAction->isChecked()
|
|
? Qt::AlignRight
|
|
: Qt::AlignHCenter;
|
|
if (model()->setHeaderData(menuIsForCol, Qt::Horizontal, al, Qt::TextAlignmentRole)) {
|
|
header()->reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#include "moc_tableview.cpp"
|