1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qrangecollection.h"
41#include "qrangecollection_p.h"
42
43#include <QtCore/qstack.h>
44
45QT_BEGIN_NAMESPACE
46
47void QRangeCollectionPrivate::mergeIntervals()
48{
49 const int count = intervals.count();
50
51 if (count <= 0)
52 return;
53
54 std::sort(intervals.begin(), intervals.end());
55
56 QStack<QPair<int, int>> stack;
57 stack.push(intervals[0]);
58
59 for (int i = 1; i < count; i++) {
60 QPair<int, int> &top = stack.top();
61
62 if (top.second < intervals[i].first)
63 stack.push(intervals[i]);
64 else if (top.second < intervals[i].second)
65 top.second = intervals[i].second;
66 }
67
68 std::sort(intervals.begin(), intervals.end());
69
70 intervals = stack;
71}
72
73/*!
74 \class QRangeCollection
75 \brief The QRangeCollection class represents a collection of decimal intervals.
76 \inmodule QtGui
77 \ingroup painting
78 \since 6.0
79
80 QRangeCollection manages a set of decimal intervals.
81
82 Use QPrinter::rangeCollection() to access the collection of page ranges
83 associated with a QPrinter.
84*/
85
86QRangeCollection::QRangeCollection()
87 : d_ptr(new QRangeCollectionPrivate(this))
88{
89}
90
91/*!
92 Destroys the collection.
93*/
94QRangeCollection::~QRangeCollection()
95{
96}
97
98/*!
99 Inserts a single number \a pageNumber into the collection.
100 */
101void QRangeCollection::addPage(int pageNumber)
102{
103 Q_D(QRangeCollection);
104 if (pageNumber <= 0) {
105 qWarning("QRangeCollection::addPage: 'pageNumber' must be greater than 0");
106 return;
107 }
108 d->intervals.append(qMakePair(pageNumber, pageNumber));
109 d->mergeIntervals();
110}
111
112/*!
113 Inserts a range specified with \a from and \a to into the collection.
114 */
115void QRangeCollection::addRange(int from, int to)
116{
117 Q_D(QRangeCollection);
118 if (from <= 0 || to <= 0) {
119 qWarning("QRangeCollection::addRange: 'from' and 'to' must be greater than 0");
120 return;
121 }
122 if (to < from) {
123 qWarning("QRangeCollection::addRange: 'from' must be less than or equal to 'to'");
124 std::swap(from, to);
125 }
126 d->intervals.append(qMakePair(from, to));
127 d->mergeIntervals();
128}
129
130/*!
131 Returns a list with the values of the ranges used in this collection.
132 */
133QList<QPair<int, int>> QRangeCollection::toList() const
134{
135 Q_D(const QRangeCollection);
136 return d->intervals.toList();
137}
138
139/*!
140 * Removes all ranges from this collection.
141 */
142void QRangeCollection::clear()
143{
144 Q_D(QRangeCollection);
145 d->intervals.clear();
146}
147
148/*!
149 Constructs the range collection from a string representation of \a ranges.
150
151 \code
152 QPrinter printer;
153 printer->rangeCollection()->parse("1-3,6-7");
154 \endcode
155
156 Returns \c true on success.
157 */
158bool QRangeCollection::parse(const QString &ranges)
159{
160 Q_D(QRangeCollection);
161 const QStringList items = ranges.split(u',');
162 for (const QString &item : items) {
163 if (item.isEmpty()) {
164 d->intervals.clear();
165 return false;
166 }
167
168 if (item.contains(QLatin1Char('-'))) {
169 const QStringList rangeItems = item.split(u'-');
170 if (rangeItems.count() != 2) {
171 d->intervals.clear();
172 return false;
173 }
174
175 bool ok;
176 const int number1 = rangeItems[0].toInt(&ok);
177 if (!ok) {
178 d->intervals.clear();
179 return false;
180 }
181
182 const int number2 = rangeItems[1].toInt(&ok);
183 if (!ok) {
184 d->intervals.clear();
185 return false;
186 }
187
188 if (number1 < 1 || number2 < 1 || number2 < number1) {
189 d->intervals.clear();
190 return false;
191 }
192
193 d->intervals.append(qMakePair(number1, number2));
194
195 } else {
196 bool ok;
197 const int number = item.toInt(&ok);
198 if (!ok) {
199 d->intervals.clear();
200 return false;
201 }
202
203 if (number < 1) {
204 d->intervals.clear();
205 return false;
206 }
207
208 d->intervals.append(qMakePair(number, number));
209 }
210 }
211
212 d->mergeIntervals();
213 return true;
214}
215
216/*!
217 Returns the string representation of the ranges in the collection.
218 */
219QString QRangeCollection::toString() const
220{
221 Q_D(const QRangeCollection);
222 QString result;
223
224 for (const QPair<int, int> &pair : d->intervals) {
225 if (!result.isEmpty())
226 result += QLatin1Char(',');
227
228 if (pair.first == pair.second)
229 result += QString::number(pair.first);
230 else
231 result += QStringLiteral("%1-%2").arg(pair.first).arg(pair.second);
232 }
233
234 return result;
235}
236
237/*!
238 \fn bool QRangeCollection::contains(const int pageNumber) const
239
240 Returns \c true if the collection contains an occurrence
241 or a bounding range of \a pageNumber; otherwise returns
242 \c false.
243 */
244bool QRangeCollection::contains(int pageNumber) const
245{
246 Q_D(const QRangeCollection);
247 for (const QPair<int, int> &pair : d->intervals) {
248 if (pair.first <= pageNumber && pair.second >= pageNumber)
249 return true;
250 }
251 return false;
252}
253
254/*!
255 Returns \c true if the collection is empty; otherwise returns \c false.
256*/
257bool QRangeCollection::isEmpty() const
258{
259 Q_D(const QRangeCollection);
260 return d->intervals.isEmpty();
261}
262
263/*!
264 Returns the index of the first page covered by the range collection.
265*/
266int QRangeCollection::firstPage() const
267{
268 Q_D(const QRangeCollection);
269 if (d->intervals.isEmpty())
270 return 0;
271 return d->intervals.first().first;
272}
273
274/*!
275 Returns the index of the last page covered by the range collection.
276*/
277int QRangeCollection::lastPage() const
278{
279 Q_D(const QRangeCollection);
280 if (d->intervals.isEmpty())
281 return 0;
282 return d->intervals.last().second;
283}
284
285QT_END_NAMESPACE
286