2018-02-27 18:06:05 +01:00
|
|
|
/*
|
|
|
|
* Strawberry Music Player
|
|
|
|
* This file was part of Clementine.
|
|
|
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
2021-03-20 21:14:47 +01:00
|
|
|
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
2018-02-27 18:06:05 +01:00
|
|
|
*
|
|
|
|
* Strawberry 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 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
2018-08-09 18:39:44 +02:00
|
|
|
*
|
2018-02-27 18:06:05 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <algorithm>
|
|
|
|
#include <numeric>
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QtGlobal>
|
|
|
|
#include <QWidget>
|
|
|
|
#include <QHeaderView>
|
|
|
|
#include <QAbstractItemModel>
|
|
|
|
#include <QIODevice>
|
|
|
|
#include <QArrayData>
|
|
|
|
#include <QByteArray>
|
2018-02-27 18:06:05 +01:00
|
|
|
#include <QDataStream>
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QList>
|
2020-02-09 02:29:35 +01:00
|
|
|
#include <QResizeEvent>
|
|
|
|
#include <QMouseEvent>
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "stretchheaderview.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
const int StretchHeaderView::kMinimumColumnWidth = 10;
|
|
|
|
const int StretchHeaderView::kMagicNumber = 0x502c950f;
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
StretchHeaderView::StretchHeaderView(const Qt::Orientation orientation, QWidget *parent)
|
2018-02-27 18:06:05 +01:00
|
|
|
: QHeaderView(orientation, parent),
|
|
|
|
stretch_enabled_(false),
|
2021-07-11 07:40:57 +02:00
|
|
|
in_mouse_move_event_(false) {
|
2020-09-16 00:01:16 +02:00
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(this, &StretchHeaderView::sectionResized, this, &StretchHeaderView::SectionResized);
|
2020-09-16 00:01:16 +02:00
|
|
|
setMinimumSectionSize(kMinimumColumnWidth);
|
2021-10-17 02:32:34 +02:00
|
|
|
setTextElideMode(Qt::ElideRight);
|
2020-09-16 00:01:16 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
void StretchHeaderView::setModel(QAbstractItemModel *model) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
QHeaderView::setModel(model);
|
|
|
|
|
|
|
|
if (stretch_enabled_) {
|
|
|
|
column_widths_.resize(count());
|
|
|
|
std::fill(column_widths_.begin(), column_widths_.end(), 1.0 / count());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-06-12 20:53:23 +02:00
|
|
|
void StretchHeaderView::NormaliseWidths(const QList<int> §ions) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!stretch_enabled_) return;
|
|
|
|
|
|
|
|
const ColumnWidthType total_sum = std::accumulate(column_widths_.begin(), column_widths_.end(), 0.0);
|
|
|
|
ColumnWidthType selected_sum = total_sum;
|
|
|
|
|
|
|
|
if (!sections.isEmpty()) {
|
|
|
|
selected_sum = 0.0;
|
2021-08-23 21:21:08 +02:00
|
|
|
for (int i = 0; i < count(); ++i)
|
|
|
|
if (sections.contains(i)) {
|
2018-02-27 18:06:05 +01:00
|
|
|
selected_sum += column_widths_[i];
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (total_sum != 0.0 && !qFuzzyCompare(total_sum, 1.0)) {
|
|
|
|
const ColumnWidthType mult = (selected_sum + (1.0 - total_sum)) / selected_sum;
|
2021-08-23 21:21:08 +02:00
|
|
|
for (int i = 0; i < column_widths_.count(); ++i) {
|
|
|
|
if (sections.isEmpty() || sections.contains(i)) {
|
2018-02-27 18:06:05 +01:00
|
|
|
column_widths_[i] *= mult;
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 19:55:00 +02:00
|
|
|
void StretchHeaderView::UpdateWidths(const QList<int> §ions) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-08-23 21:21:08 +02:00
|
|
|
if (!stretch_enabled_) return;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
ColumnWidthType total_w = 0.0;
|
|
|
|
|
|
|
|
for (int i = 0; i < column_widths_.count(); ++i) {
|
|
|
|
const ColumnWidthType w = column_widths_[i];
|
|
|
|
int pixels = w * width();
|
|
|
|
|
|
|
|
total_w += w;
|
|
|
|
|
2021-08-23 21:21:08 +02:00
|
|
|
if (!sections.isEmpty() && !sections.contains(i)) {
|
2018-02-27 18:06:05 +01:00
|
|
|
continue;
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-08-23 21:21:08 +02:00
|
|
|
if (pixels == 0 && !isSectionHidden(i)) {
|
2018-02-27 18:06:05 +01:00
|
|
|
hideSection(i);
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
else if (pixels != 0 && isSectionHidden(i)) {
|
|
|
|
showSection(i);
|
|
|
|
}
|
|
|
|
|
2021-08-23 21:21:08 +02:00
|
|
|
if (pixels != 0) {
|
2018-02-27 18:06:05 +01:00
|
|
|
resizeSection(i, pixels);
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
void StretchHeaderView::HideSection(const int logical) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
// Would this hide the last section?
|
|
|
|
bool all_hidden = true;
|
2021-08-23 21:21:08 +02:00
|
|
|
for (int i = 0; i < count(); ++i) {
|
2018-02-27 18:06:05 +01:00
|
|
|
if (i != logical && !isSectionHidden(i) && sectionSize(i) > 0) {
|
|
|
|
all_hidden = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (all_hidden) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!stretch_enabled_) {
|
|
|
|
hideSection(logical);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
column_widths_[logical] = 0.0;
|
|
|
|
NormaliseWidths();
|
|
|
|
UpdateWidths();
|
2020-08-29 19:55:00 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void StretchHeaderView::ShowSection(int logical) {
|
|
|
|
|
|
|
|
if (!stretch_enabled_) {
|
|
|
|
showSection(logical);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// How many sections are visible already?
|
|
|
|
int visible_count = 0;
|
2021-08-23 21:21:08 +02:00
|
|
|
for (int i = 0; i < count(); ++i) {
|
|
|
|
if (!isSectionHidden(i)) {
|
2021-07-11 09:49:38 +02:00
|
|
|
++visible_count;
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
column_widths_[logical] = visible_count == 0 ? 1.0 : 1.0 / visible_count;
|
2018-02-27 18:06:05 +01:00
|
|
|
NormaliseWidths();
|
|
|
|
UpdateWidths();
|
2020-08-29 19:55:00 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
void StretchHeaderView::SetSectionHidden(const int logical, const bool hidden) {
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
if (hidden) {
|
|
|
|
HideSection(logical);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ShowSection(logical);
|
|
|
|
}
|
2020-04-23 21:08:28 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2021-02-02 21:08:58 +01:00
|
|
|
void StretchHeaderView::resizeEvent(QResizeEvent *e) {
|
2020-04-23 21:08:28 +02:00
|
|
|
|
2021-02-02 21:08:58 +01:00
|
|
|
QHeaderView::resizeEvent(e);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!stretch_enabled_) return;
|
|
|
|
|
|
|
|
UpdateWidths();
|
2020-04-23 21:08:28 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
void StretchHeaderView::mouseMoveEvent(QMouseEvent *e) {
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
in_mouse_move_event_ = true;
|
|
|
|
QHeaderView::mouseMoveEvent(e);
|
|
|
|
in_mouse_move_event_ = false;
|
2020-04-23 21:08:28 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
void StretchHeaderView::SectionResized(const int logical, const int, const int new_size) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!stretch_enabled_) return;
|
|
|
|
|
|
|
|
if (in_mouse_move_event_) {
|
|
|
|
// Update this section's proportional width
|
|
|
|
column_widths_[logical] = ColumnWidthType(new_size) / width();
|
|
|
|
|
|
|
|
// Find the visible sections to the right of the section that's being resized
|
|
|
|
int visual = visualIndex(logical);
|
|
|
|
QList<int> logical_sections_to_resize;
|
|
|
|
for (int i = 0; i < count(); ++i) {
|
2021-08-23 21:21:08 +02:00
|
|
|
if (!isSectionHidden(i) && visualIndex(i) > visual) {
|
2018-02-27 18:06:05 +01:00
|
|
|
logical_sections_to_resize << i;
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Resize just those columns
|
|
|
|
if (!logical_sections_to_resize.isEmpty()) {
|
|
|
|
in_mouse_move_event_ = false;
|
|
|
|
UpdateWidths(logical_sections_to_resize);
|
|
|
|
NormaliseWidths(logical_sections_to_resize);
|
|
|
|
in_mouse_move_event_ = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void StretchHeaderView::ToggleStretchEnabled() {
|
|
|
|
SetStretchEnabled(!is_stretch_enabled());
|
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
void StretchHeaderView::SetStretchEnabled(const bool enabled) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
stretch_enabled_ = enabled;
|
|
|
|
|
|
|
|
if (enabled) {
|
2020-10-17 17:29:09 +02:00
|
|
|
// Initialize the list of widths from the current state of the widget
|
2018-02-27 18:06:05 +01:00
|
|
|
column_widths_.resize(count());
|
|
|
|
for (int i = 0; i < count(); ++i) {
|
|
|
|
column_widths_[i] = ColumnWidthType(sectionSize(i)) / width();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stretch the columns to fill the widget
|
|
|
|
NormaliseWidths();
|
|
|
|
UpdateWidths();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit StretchEnabledChanged(enabled);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
void StretchHeaderView::SetColumnWidth(const int logical, const ColumnWidthType width) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (!stretch_enabled_) return;
|
|
|
|
|
|
|
|
column_widths_[logical] = width;
|
|
|
|
|
|
|
|
QList<int> other_columns;
|
2021-08-23 21:21:08 +02:00
|
|
|
for (int i = 0; i < count(); ++i) {
|
|
|
|
if (!isSectionHidden(i) && i != logical) {
|
2018-02-27 18:06:05 +01:00
|
|
|
other_columns << i;
|
2021-08-23 21:21:08 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-23 21:08:28 +02:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
NormaliseWidths(other_columns);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
bool StretchHeaderView::RestoreState(const QByteArray &sdata) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2020-04-23 21:08:28 +02:00
|
|
|
QDataStream s(sdata);
|
2018-05-01 00:41:33 +02:00
|
|
|
s.setVersion(QDataStream::Qt_5_6);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
int magic_number = 0;
|
|
|
|
s >> magic_number;
|
|
|
|
|
|
|
|
if (magic_number != kMagicNumber || s.atEnd()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<int> pixel_widths;
|
|
|
|
QList<int> visual_indices;
|
|
|
|
int sort_indicator_order = Qt::AscendingOrder;
|
|
|
|
int sort_indicator_section = 0;
|
|
|
|
|
|
|
|
s >> stretch_enabled_;
|
|
|
|
s >> pixel_widths;
|
|
|
|
s >> visual_indices;
|
|
|
|
s >> column_widths_;
|
|
|
|
s >> sort_indicator_order;
|
|
|
|
s >> sort_indicator_section;
|
|
|
|
|
|
|
|
setSortIndicator(sort_indicator_section, Qt::SortOrder(sort_indicator_order));
|
|
|
|
|
|
|
|
const int persisted_column_count = qMin(qMin(visual_indices.count(), pixel_widths.count()), column_widths_.count());
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
// Set column visible state, visual indices and, if we're not in stretch mode, pixel widths.
|
2018-02-27 18:06:05 +01:00
|
|
|
for (int i = 0; i < count() && i < persisted_column_count; ++i) {
|
|
|
|
setSectionHidden(i, pixel_widths[i] <= kMinimumColumnWidth);
|
|
|
|
moveSection(visualIndex(visual_indices[i]), i);
|
|
|
|
|
|
|
|
if (!stretch_enabled_) {
|
|
|
|
resizeSection(i, pixel_widths[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Have we added more columns since the last time?
|
|
|
|
while (column_widths_.count() < count()) {
|
|
|
|
column_widths_ << 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stretch_enabled_) {
|
2018-05-01 00:41:33 +02:00
|
|
|
// In stretch mode, we've already set the proportional column widths so apply them now.
|
2018-02-27 18:06:05 +01:00
|
|
|
UpdateWidths();
|
|
|
|
}
|
|
|
|
|
|
|
|
emit StretchEnabledChanged(stretch_enabled_);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray StretchHeaderView::SaveState() const {
|
|
|
|
|
|
|
|
QByteArray ret;
|
|
|
|
QDataStream s(&ret, QIODevice::WriteOnly);
|
|
|
|
|
|
|
|
QList<int> pixel_widths;
|
|
|
|
QList<int> visual_indices;
|
|
|
|
|
2021-06-20 19:04:08 +02:00
|
|
|
pixel_widths.reserve(count());
|
|
|
|
visual_indices.reserve(count());
|
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
for (int i = 0; i < count(); ++i) {
|
|
|
|
pixel_widths << sectionSize(i);
|
|
|
|
visual_indices << logicalIndex(i);
|
|
|
|
}
|
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
s.setVersion(QDataStream::Qt_5_6);
|
2019-01-01 20:07:29 +01:00
|
|
|
|
2018-02-27 18:06:05 +01:00
|
|
|
s << kMagicNumber;
|
|
|
|
|
|
|
|
s << stretch_enabled_;
|
|
|
|
s << pixel_widths;
|
|
|
|
s << visual_indices;
|
|
|
|
s << column_widths_;
|
|
|
|
s << int(sortIndicatorOrder());
|
|
|
|
s << sortIndicatorSection();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
2020-08-29 19:55:00 +02:00
|
|
|
|
|
|
|
QByteArray StretchHeaderView::ResetState() {
|
|
|
|
|
|
|
|
QByteArray ret;
|
|
|
|
|
|
|
|
QDataStream s(&ret, QIODevice::WriteOnly);
|
|
|
|
s.setVersion(QDataStream::Qt_5_6);
|
|
|
|
|
|
|
|
s << kMagicNumber;
|
|
|
|
|
|
|
|
stretch_enabled_ = true;
|
|
|
|
|
|
|
|
column_widths_.resize(count());
|
|
|
|
std::fill(column_widths_.begin(), column_widths_.end(), 1.0 / count());
|
|
|
|
|
|
|
|
QList<int> visual_indices;
|
|
|
|
QList<int> pixel_widths;
|
2021-06-20 19:04:08 +02:00
|
|
|
|
|
|
|
visual_indices.reserve(count());
|
|
|
|
pixel_widths.reserve(count());
|
|
|
|
|
2021-08-23 21:21:08 +02:00
|
|
|
for (int i = 0; i < count(); ++i) {
|
2020-08-29 19:55:00 +02:00
|
|
|
pixel_widths << 10;
|
|
|
|
visual_indices << count();
|
|
|
|
}
|
|
|
|
|
|
|
|
s << stretch_enabled_;
|
|
|
|
s << pixel_widths;
|
|
|
|
s << visual_indices;
|
|
|
|
s << column_widths_;
|
|
|
|
s << int(Qt::AscendingOrder);
|
|
|
|
s << 0;
|
|
|
|
|
|
|
|
RestoreState(ret);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|