1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QHEADERVIEW_P_H
5#define QHEADERVIEW_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtWidgets/private/qtwidgetsglobal_p.h>
19#include "qheaderview.h"
20#include "private/qabstractitemview_p.h"
21
22#include "QtCore/qbitarray.h"
23#include "QtWidgets/qapplication.h"
24#if QT_CONFIG(label)
25#include "QtWidgets/qlabel.h"
26#endif
27
28#include <array>
29
30QT_REQUIRE_CONFIG(itemviews);
31
32QT_BEGIN_NAMESPACE
33
34// Currently we support huge models with no memory per section if people are not resizing sections and not ordering sections.
35// This enum is unlikely to be public later on. (Either we go for a huge model bool or another ("subset") enum not containing the initial mode).
36
37enum HeaderMode
38{
39 InitialNoSectionMemoryUsage, // Initial state - we don't use any memory per section until needed (needed on resize, swap, move, hide etc)
40 FlexibleWithSectionMemoryUsage, // user can hide, resize and reorder sections at the cost of memory usage.
41};
42
43class QHeaderViewPrivate: public QAbstractItemViewPrivate
44{
45 Q_DECLARE_PUBLIC(QHeaderView)
46
47public:
48 enum StateVersion { VersionMarker = 0xff };
49
50 QHeaderViewPrivate()
51 : state(NoState),
52 headerOffset(0),
53 sortIndicatorOrder(Qt::DescendingOrder),
54 sortIndicatorSection(0),
55 sortIndicatorShown(false),
56 sortIndicatorClearable(false),
57 lastPos(-1),
58 firstPos(-1),
59 originalSize(-1),
60 section(-1),
61 target(-1),
62 firstPressed(-1),
63 pressed(-1),
64 hover(-1),
65 length(0),
66 preventCursorChangeInSetOffset(false),
67 movableSections(false),
68 clickableSections(false),
69 highlightSelected(false),
70 stretchLastSection(false),
71 cascadingResizing(false),
72 resizeRecursionBlock(false),
73 allowUserMoveOfSection0(true), // will be false for QTreeView and true for QTableView
74 customDefaultSectionSize(false),
75 stretchSections(0),
76 contentsSections(0),
77 minimumSectionSize(-1),
78 maximumSectionSize(-1),
79 lastSectionSize(0),
80 lastSectionLogicalIdx(-1), // Only trust when we stretch last section
81 sectionIndicatorOffset(0),
82#if QT_CONFIG(label)
83 sectionIndicator(nullptr),
84#endif
85 globalResizeMode(QHeaderView::Interactive),
86 sectionStartposRecalc(true),
87 resizeContentsPrecision(1000)
88 {}
89
90
91 int lastVisibleVisualIndex() const;
92 void restoreSizeOnPrevLastSection();
93 void setNewLastSection(int visualIndexForLastSection);
94 void maybeRestorePrevLastSectionAndStretchLast();
95 int sectionHandleAt(int position);
96 void setupSectionIndicator(int section, int position);
97 void updateSectionIndicator(int section, int position);
98 void updateHiddenSections(int logicalFirst, int logicalLast);
99 void resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode = false);
100 void sectionsRemoved(const QModelIndex &,int,int);
101 void sectionsAboutToBeMoved(const QModelIndex &sourceParent, int logicalStart,
102 int logicalEnd, const QModelIndex &destinationParent,
103 int logicalDestination);
104 void sectionsMoved(const QModelIndex &sourceParent, int logicalStart,
105 int logicalEnd, const QModelIndex &destinationParent,
106 int logicalDestination);
107 void sectionsAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(),
108 QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
109 void sectionsChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(),
110 QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
111
112 bool isSectionSelected(int section) const;
113 bool isFirstVisibleSection(int section) const;
114 bool isLastVisibleSection(int section) const;
115
116 inline bool rowIntersectsSelection(int row) const {
117 return (selectionModel ? selectionModel->rowIntersectsSelection(row, parent: root) : false);
118 }
119
120 inline bool columnIntersectsSelection(int column) const {
121 return (selectionModel ? selectionModel->columnIntersectsSelection(column, parent: root) : false);
122 }
123
124 inline bool sectionIntersectsSelection(int logical) const {
125 return (orientation == Qt::Horizontal ? columnIntersectsSelection(column: logical) : rowIntersectsSelection(row: logical));
126 }
127
128 inline bool isRowSelected(int row) const {
129 return (selectionModel ? selectionModel->isRowSelected(row, parent: root) : false);
130 }
131
132 inline bool isColumnSelected(int column) const {
133 return (selectionModel ? selectionModel->isColumnSelected(column, parent: root) : false);
134 }
135
136 inline void prepareSectionSelected() {
137 if (!selectionModel || !selectionModel->hasSelection())
138 sectionSelected.clear();
139 else if (sectionSelected.size() != sectionCount() * 2)
140 sectionSelected.fill(aval: false, asize: sectionCount() * 2);
141 else sectionSelected.fill(aval: false);
142 }
143
144 inline int sectionCount() const {
145 return noSectionMemoryUsage() ? countInNoSectionItemsMode : sectionItems.size();
146 }
147
148 inline bool reverse() const {
149 return orientation == Qt::Horizontal && q_func()->isRightToLeft();
150 }
151
152 inline int logicalIndex(int visualIndex) const {
153 return logicalIndices.isEmpty() ? visualIndex : logicalIndices.at(i: visualIndex);
154 }
155
156 inline int visualIndex(int logicalIndex) const {
157 return visualIndices.isEmpty() ? logicalIndex : visualIndices.at(i: logicalIndex);
158 }
159
160 inline void setDefaultValues(Qt::Orientation o) {
161 orientation = o;
162 updateDefaultSectionSizeFromStyle();
163 defaultAlignment = (o == Qt::Horizontal
164 ? Qt::Alignment(Qt::AlignCenter)
165 : Qt::AlignLeft|Qt::AlignVCenter);
166 }
167
168 inline bool isVisualIndexHidden(int visual) const {
169 return !noSectionMemoryUsage() && sectionItems.at(i: visual).isHidden;
170 }
171
172 inline void setVisualIndexHidden(int visual, bool hidden) {
173 sectionItems[visual].isHidden = hidden;
174 }
175
176 inline bool hasAutoResizeSections() const {
177 return stretchSections || stretchLastSection || contentsSections;
178 }
179
180 QStyleOptionHeader getStyleOption() const;
181
182 inline void invalidateCachedSizeHint() const {
183 cachedSizeHint = QSize();
184 }
185
186 inline void initializeIndexMapping() const {
187 if (visualIndices.size() != sectionCount()
188 || logicalIndices.size() != sectionCount()) {
189 visualIndices.resize(size: sectionCount());
190 logicalIndices.resize(size: sectionCount());
191 for (int s = 0; s < sectionCount(); ++s) {
192 visualIndices[s] = s;
193 logicalIndices[s] = s;
194 }
195 }
196 }
197
198 inline void clearCascadingSections() {
199 firstCascadingSection = sectionItems.size();
200 lastCascadingSection = 0;
201 cascadingSectionSize.clear();
202 }
203
204 inline void saveCascadingSectionSize(int visual, int size) {
205 if (!cascadingSectionSize.contains(key: visual)) {
206 cascadingSectionSize.insert(key: visual, value: size);
207 firstCascadingSection = qMin(a: firstCascadingSection, b: visual);
208 lastCascadingSection = qMax(a: lastCascadingSection, b: visual);
209 }
210 }
211
212 inline bool sectionIsCascadable(int visual) const {
213 return headerSectionResizeMode(visual) == QHeaderView::Interactive;
214 }
215
216 inline int modelSectionCount() const {
217 return (orientation == Qt::Horizontal
218 ? model->columnCount(parent: root)
219 : model->rowCount(parent: root));
220 }
221
222 inline void doDelayedResizeSections() {
223 if (!delayedResize.isActive())
224 delayedResize.start(msec: 0, obj: q_func());
225 }
226
227 inline void executePostedResize() const {
228 if (delayedResize.isActive() && state == NoState) {
229 const_cast<QHeaderView*>(q_func())->resizeSections();
230 }
231 }
232
233 inline void disconnectModel()
234 {
235 for (const QMetaObject::Connection &connection : modelConnections)
236 QObject::disconnect(connection);
237 }
238
239 void clear();
240 void flipSortIndicator(int section);
241 Qt::SortOrder defaultSortOrderForSection(int section) const;
242 void cascadingResize(int visual, int newSize);
243
244 enum State { NoState, ResizeSection, MoveSection, SelectSections, NoClear } state;
245
246 int headerOffset;
247 Qt::Orientation orientation;
248 Qt::SortOrder sortIndicatorOrder;
249 int sortIndicatorSection;
250 bool sortIndicatorShown;
251 bool sortIndicatorClearable;
252
253 mutable QList<int> visualIndices; // visualIndex = visualIndices.at(logicalIndex)
254 mutable QList<int> logicalIndices; // logicalIndex = row or column in the model
255 mutable QBitArray sectionSelected; // from logical index to bit
256 mutable QHash<int, int> hiddenSectionSize; // from logical index to section size
257 mutable QHash<int, int> cascadingSectionSize; // from visual index to section size
258 mutable QSize cachedSizeHint;
259 mutable QBasicTimer delayedResize;
260
261 int firstCascadingSection;
262 int lastCascadingSection;
263
264 int lastPos;
265 int firstPos;
266 int originalSize;
267 int section; // used for resizing and moving sections
268 int target;
269 int firstPressed;
270 int pressed;
271 int hover;
272
273 int length;
274 bool preventCursorChangeInSetOffset;
275 bool movableSections;
276 bool clickableSections;
277 bool highlightSelected;
278 bool stretchLastSection;
279 bool cascadingResizing;
280 bool resizeRecursionBlock;
281 bool allowUserMoveOfSection0;
282 bool customDefaultSectionSize;
283 int stretchSections;
284 int contentsSections;
285 int defaultSectionSize;
286 int oldDefaultSectionSize = -1;
287 int minimumSectionSize;
288 int maximumSectionSize;
289 int lastSectionSize;
290 int lastSectionLogicalIdx; // Only trust if we stretch LastSection
291 int sectionIndicatorOffset;
292 Qt::Alignment defaultAlignment;
293#if QT_CONFIG(label)
294 QLabel *sectionIndicator;
295#endif
296 QHeaderView::ResizeMode globalResizeMode;
297 mutable bool sectionStartposRecalc;
298 int resizeContentsPrecision;
299 // header sections
300
301 struct SectionItem {
302 uint size : 20;
303 uint isHidden : 1;
304 uint resizeMode : 5; // (holding QHeaderView::ResizeMode)
305 uint currentlyUnusedPadding : 6;
306
307 union { // This union is made in order to save space and ensure good vector performance (on remove)
308 mutable int calculated_startpos; // <- this is the primary used member.
309 mutable int tmpLogIdx; // When one of these 'tmp'-members has been used we call
310 int tmpDataStreamSectionCount; // recalcSectionStartPos() or set sectionStartposRecalc to true
311 }; // to ensure that calculated_startpos will be calculated afterwards.
312
313 inline SectionItem() : size(0), isHidden(0), resizeMode(QHeaderView::Interactive) {}
314 inline SectionItem(int length, QHeaderView::ResizeMode mode)
315 : size(length), isHidden(0), resizeMode(mode), calculated_startpos(-1) {}
316 inline int sectionSize() const { return size; }
317 inline int calculatedEndPos() const { return calculated_startpos + size; }
318#ifndef QT_NO_DATASTREAM
319 inline void write(QDataStream &out) const
320 { out << static_cast<int>(size); out << 1; out << (int)resizeMode; }
321 inline void read(QDataStream &in)
322 { int m; in >> m; size = m; in >> tmpDataStreamSectionCount; in >> m; resizeMode = m; }
323#endif
324 };
325
326 QList<SectionItem> sectionItems;
327
328 HeaderMode headerMode = HeaderMode::InitialNoSectionMemoryUsage;
329 qsizetype countInNoSectionItemsMode = 0;
330 inline bool noSectionMemoryUsage() const
331 {
332 return (headerMode == HeaderMode::InitialNoSectionMemoryUsage);
333 }
334
335 inline void switchToFlexibleModeWithSectionMemoryUsage()
336 {
337 setHeaderMode(HeaderMode::FlexibleWithSectionMemoryUsage);
338 }
339
340 void updateCountInNoSectionItemsMode(int newCount);
341 void setHeaderMode(HeaderMode mode);
342
343 struct LayoutChangeItem {
344 QPersistentModelIndex index;
345 SectionItem section;
346 };
347 QList<LayoutChangeItem> layoutChangePersistentSections;
348 std::array<QMetaObject::Connection, 8> modelConnections;
349
350 void createSectionItems(int start, int end, int sectionSize, QHeaderView::ResizeMode mode);
351 void removeSectionsFromSectionItems(int start, int end);
352 void resizeSectionItem(int visualIndex, int oldSize, int newSize);
353 void setDefaultSectionSize(int size);
354 void updateDefaultSectionSizeFromStyle();
355 void recalcSectionStartPos() const; // not really const
356
357 inline int headerLength() const { // for debugging
358 int len = 0;
359 for (const auto &section : sectionItems)
360 len += section.size;
361 return len;
362 }
363
364 QBitArray sectionsHiddenToBitVector() const
365 {
366 QBitArray sectionHidden;
367 if (!hiddenSectionSize.isEmpty()) {
368 sectionHidden.resize(size: sectionItems.size());
369 for (int u = 0; u < sectionItems.size(); ++u)
370 sectionHidden[u] = sectionItems.at(i: u).isHidden;
371 }
372 return sectionHidden;
373 }
374
375 void setHiddenSectionsFromBitVector(const QBitArray &sectionHidden) {
376 SectionItem *sectionData = sectionItems.data();
377 for (int i = 0; i < sectionHidden.size(); ++i)
378 sectionData[i].isHidden = sectionHidden.at(i);
379 }
380
381 int headerSectionSize(int visual) const;
382 int headerSectionPosition(int visual) const;
383 int headerVisualIndexAt(int position) const;
384
385 // resize mode
386 void setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode);
387 QHeaderView::ResizeMode headerSectionResizeMode(int visual) const;
388 void setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode);
389
390 // other
391 int viewSectionSizeHint(int logical) const;
392 int adjustedVisualIndex(int visualIndex) const;
393 void setScrollOffset(const QScrollBar *scrollBar, QAbstractItemView::ScrollMode scrollMode);
394 void updateSectionsBeforeAfter(int logical);
395
396#ifndef QT_NO_DATASTREAM
397 void write(QDataStream &out) const;
398 bool read(QDataStream &in);
399#endif
400
401};
402Q_DECLARE_TYPEINFO(QHeaderViewPrivate::SectionItem, Q_PRIMITIVE_TYPE);
403Q_DECLARE_TYPEINFO(QHeaderViewPrivate::LayoutChangeItem, Q_RELOCATABLE_TYPE);
404
405QT_END_NAMESPACE
406
407#endif // QHEADERVIEW_P_H
408

source code of qtbase/src/widgets/itemviews/qheaderview_p.h