1// Copyright (C) 2020 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#include "qpageranges.h"
5#include "qpageranges_p.h"
6
7#include <QtCore/qstack.h>
8#include <QtCore/qdebug.h>
9#include <QtCore/qdatastream.h>
10
11QT_BEGIN_NAMESPACE
12
13QT_IMPL_METATYPE_EXTERN(QPageRanges)
14
15void QPageRangesPrivate::mergeIntervals()
16{
17 const int count = intervals.size();
18
19 if (count <= 1)
20 return;
21
22 std::sort(first: intervals.begin(), last: intervals.end());
23
24 QStack<QPageRanges::Range> stack;
25 stack.push(t: intervals[0]);
26
27 for (int i = 1; i < count; ++i) {
28 QPageRanges::Range &top = stack.top();
29
30 if (top.to < intervals[i].from - 1)
31 stack.push(t: intervals[i]);
32 else if (top.to < intervals[i].to)
33 top.to = intervals[i].to;
34 }
35
36 intervals = stack;
37}
38
39QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QPageRangesPrivate)
40
41/*!
42 \class QPageRanges
43 \brief The QPageRanges class represents a collection of page ranges.
44 \inmodule QtGui
45 \ingroup painting
46 \ingroup printing
47 \ingroup shared
48 \since 6.0
49
50 Use QPagedPaintDevice::pageRanges() to access the collection of page ranges
51 associated with a paged device.
52*/
53
54/*!
55 Constructs an empty QPageRanges object.
56*/
57QPageRanges::QPageRanges() = default;
58
59/*!
60 Constructs a QPageRanges object by copying \a other.
61*/
62QPageRanges::QPageRanges(const QPageRanges &other) noexcept = default;
63
64/*!
65 \fn QPageRanges::QPageRanges(QPageRanges &&other)
66
67 Constructs a QPageRanges object by moving from \a other.
68*/
69
70/*!
71 Destroys the page ranges.
72*/
73QPageRanges::~QPageRanges() = default;
74
75/*!
76 Assigns \a other to this QPageRanges object.
77*/
78QPageRanges &QPageRanges::operator=(const QPageRanges &other) noexcept = default;
79
80/*!
81 \fn QPageRanges &QPageRanges::operator=(QPageRanges &&other)
82 Moves \a other into this QPageRanges object.
83*/
84
85/*!
86 Adds the single page \a pageNumber to the ranges.
87
88 \note Page numbers start with 1. Attempts to add page numbers
89 smaller than 1 will be ignored with a warning.
90*/
91void QPageRanges::addPage(int pageNumber)
92{
93 if (pageNumber <= 0) {
94 qWarning(msg: "QPageRanges::addPage: 'pageNumber' must be greater than 0");
95 return;
96 }
97
98 detach();
99 d->intervals.append(t: { .from: pageNumber, .to: pageNumber });
100 d->mergeIntervals();
101}
102
103/*!
104 Adds the range specified with \a from and \a to to the ranges.
105
106 \note Page numbers start with 1. Attempts to add page numbers
107 smaller than 1 will be ignored with a warning.
108*/
109void QPageRanges::addRange(int from, int to)
110{
111 if (from <= 0 || to <= 0) {
112 qWarning(msg: "QPageRanges::addRange: 'from' and 'to' must be greater than 0");
113 return;
114 }
115 if (to < from)
116 std::swap(a&: from, b&: to);
117
118 detach();
119 d->intervals.append(t: {.from: from, .to: to});
120 d->mergeIntervals();
121}
122
123/*!
124 Returns a list with the values of the ranges.
125*/
126QList<QPageRanges::Range> QPageRanges::toRangeList() const
127{
128 if (d)
129 return d->intervals;
130 return QList<QPageRanges::Range>{};
131}
132
133/*!
134 Removes all page ranges.
135*/
136void QPageRanges::clear()
137{
138 d.reset();
139}
140
141/*!
142 Constructs and returns a QPageRanges object populated with the
143 \a ranges from the string representation.
144
145 \code
146 QPrinter printer;
147 QPageRanges ranges = QPageRanges::fromString("1-3,6-7");
148 printer.setPageRanges(ranges);
149 \endcode
150
151 In case of parsing error, returns an empty QPageRanges object.
152
153 \sa isEmpty()
154*/
155QPageRanges QPageRanges::fromString(const QString &ranges)
156{
157 QList<Range> intervals;
158 const QStringList items = ranges.split(sep: u',');
159 for (const QString &item : items) {
160 if (item.isEmpty())
161 return QPageRanges();
162
163 if (item.contains(c: u'-')) {
164 const QStringList rangeItems = item.split(sep: u'-');
165 if (rangeItems.size() != 2)
166 return QPageRanges();
167
168 bool ok;
169 const int number1 = rangeItems[0].toInt(ok: &ok);
170 if (!ok)
171 return QPageRanges();
172
173 const int number2 = rangeItems[1].toInt(ok: &ok);
174 if (!ok)
175 return QPageRanges();
176
177 if (number1 < 1 || number2 < 1 || number2 < number1)
178 return QPageRanges();
179
180 intervals.append(t: {.from: number1, .to: number2});
181
182 } else {
183 bool ok;
184 const int number = item.toInt(ok: &ok);
185 if (!ok)
186 return QPageRanges();
187
188 if (number < 1)
189 return QPageRanges();
190
191 intervals.append(t: {.from: number, .to: number});
192 }
193 }
194
195 QPageRanges newRanges;
196 newRanges.d.reset(ptr: new QPageRangesPrivate);
197 newRanges.d->intervals = intervals;
198 newRanges.d->mergeIntervals();
199 return newRanges;
200}
201
202/*!
203 Returns the string representation of the page ranges.
204*/
205QString QPageRanges::toString() const
206{
207 if (!d)
208 return QString();
209
210 QString result;
211 for (const Range &range : d->intervals) {
212 if (!result.isEmpty())
213 result += u',';
214
215 if (range.from == range.to)
216 result += QString::number(range.from);
217 else
218 result += QStringLiteral("%1-%2").arg(a: range.from).arg(a: range.to);
219 }
220
221 return result;
222}
223
224/*!
225 \fn bool QPageRanges::contains(int pageNumber) const
226
227 Returns \c true if the ranges include the page \a pageNumber;
228 otherwise returns \c false.
229*/
230bool QPageRanges::contains(int pageNumber) const
231{
232 if (!d)
233 return false;
234
235 for (const Range &range : d->intervals) {
236 if (range.from <= pageNumber && range.to >= pageNumber)
237 return true;
238 }
239 return false;
240}
241
242/*!
243 Returns \c true if the ranges are empty; otherwise returns \c false.
244*/
245bool QPageRanges::isEmpty() const
246{
247 return !d || d->intervals.isEmpty();
248}
249
250/*!
251 Returns the index of the first page covered by the page ranges,
252 or 0 if the page ranges are empty.
253*/
254int QPageRanges::firstPage() const
255{
256 if (isEmpty())
257 return 0;
258 return d->intervals.first().from;
259}
260
261/*!
262 Returns the index of the last page covered by the page ranges,
263 or 0 if the page ranges are empty.
264*/
265int QPageRanges::lastPage() const
266{
267 if (isEmpty())
268 return 0;
269 return d->intervals.last().to;
270}
271
272/*!
273 \internal
274*/
275bool QPageRanges::isEqual(const QPageRanges &other) const noexcept
276{
277 if (d == other.d)
278 return true;
279 if (!d || !other.d)
280 return false;
281 return d->intervals == other.d->intervals;
282}
283
284/*!
285 \internal
286*/
287void QPageRanges::detach()
288{
289 if (d)
290 d.detach();
291 else
292 d.reset(ptr: new QPageRangesPrivate);
293}
294
295#if !defined(QT_NO_DATASTREAM)
296/*!
297 \fn QDataStream &operator<<(QDataStream &stream, const QPageRanges &pageRanges)
298 \relates QPageRanges
299
300 Writes \a pageRanges to \a stream as a range string.
301
302 \sa QPageRanges::toString
303*/
304
305QDataStream &operator<<(QDataStream &s, const QPageRanges &pageRanges)
306{
307 s << pageRanges.toString();
308 return s;
309}
310
311/*!
312 \fn QDataStream &operator>>(QDataStream &stream, QPageRanges &pageRanges)
313 \relates QPageRanges
314
315 Reads a page ranges string from \a stream and stores it in \a pageRanges.
316
317 \sa QPageRanges::fromString
318*/
319
320QDataStream &operator>>(QDataStream &s, QPageRanges &pageRanges)
321{
322 QString rangesString;
323 s >> rangesString;
324 pageRanges = QPageRanges::fromString(ranges: rangesString);
325 return s;
326}
327#endif // QT_NO_DATASTREAM
328
329#ifndef QT_NO_DEBUG_STREAM
330QDebug operator<<(QDebug dbg, const QPageRanges &pageRanges)
331{
332 QDebugStateSaver saver(dbg);
333 dbg.nospace();
334 dbg.noquote();
335 dbg << "QPageRanges(" << pageRanges.toString() << ")";
336
337 return dbg;
338}
339#endif
340
341/*!
342 \struct QPageRanges::Range
343 \inmodule QtGui
344 \brief The QPageRanges::Range struct holds the \c from and \c to endpoints of a range.
345
346 \sa QPageRanges::toRangeList()
347*/
348
349/*!
350 \variable QPageRanges::Range::from
351 \brief the lower endpoint of the range
352*/
353
354/*!
355 \variable QPageRanges::Range::to
356 \brief the upper endpoint of the range
357*/
358
359/*!
360 \fn bool QPageRanges::Range::contains(int pageNumber) const
361
362 Returns \c true if \a pageNumber is within the interval \c{[from, to]};
363 otherwise returns \c false.
364*/
365
366
367QT_END_NAMESPACE
368

source code of qtbase/src/gui/painting/qpageranges.cpp