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 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | QT_IMPL_METATYPE_EXTERN(QPageRanges) |
14 | |
15 | void QPageRangesPrivate::() |
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 | |
39 | QT_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 | */ |
57 | QPageRanges::() = default; |
58 | |
59 | /*! |
60 | Constructs a QPageRanges object by copying \a other. |
61 | */ |
62 | 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 | */ |
73 | QPageRanges::() = default; |
74 | |
75 | /*! |
76 | Assigns \a other to this QPageRanges object. |
77 | */ |
78 | QPageRanges &QPageRanges::(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 | */ |
91 | void QPageRanges::(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 | */ |
109 | void QPageRanges::(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 | */ |
126 | QList<QPageRanges::Range> QPageRanges::() const |
127 | { |
128 | if (d) |
129 | return d->intervals; |
130 | return QList<QPageRanges::Range>{}; |
131 | } |
132 | |
133 | /*! |
134 | Removes all page ranges. |
135 | */ |
136 | void QPageRanges::() |
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 | */ |
155 | QPageRanges QPageRanges::(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 | */ |
205 | QString QPageRanges::() 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 | */ |
230 | bool QPageRanges::(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 | */ |
245 | bool QPageRanges::() 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 | */ |
254 | int QPageRanges::() 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 | */ |
265 | int QPageRanges::() const |
266 | { |
267 | if (isEmpty()) |
268 | return 0; |
269 | return d->intervals.last().to; |
270 | } |
271 | |
272 | /*! |
273 | \internal |
274 | */ |
275 | bool QPageRanges::(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 | */ |
287 | void QPageRanges::() |
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 | |
305 | QDataStream &(QDataStream &s, const QPageRanges &) |
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 | |
320 | QDataStream &(QDataStream &s, QPageRanges &) |
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 |
330 | QDebug (QDebug dbg, const QPageRanges &) |
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 | |
367 | QT_END_NAMESPACE |
368 | |