documentmapper.cpp 13,7 КБ
Newer Older
OMP Education's avatar
OMP Education включено в состав коммита
1
2
// SPDX-FileCopyrightText: 2022 - 2023 Open Mobile Platform LLC <community@omp.ru>
// SPDX-License-Identifier: BSD-3-Clause
OMP Education's avatar
OMP Education включено в состав коммита
3
4
5
6
7

#include "basedocument.h"

#include "documentmapper.h"

OMP Education's avatar
OMP Education включено в состав коммита
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
namespace {

using SpecialRangesType = std::map<int, int>;
using SpecialRangesIterator = std::map<int, int>::iterator;

void removeFromBegin(SpecialRangesIterator it,
                     std::set<int> &specialIndexes,
                     SpecialRangesType &specialRangeIndexes)
{
    if (it->second - it->first == 1) {
        specialIndexes.insert(it->second);
        specialRangeIndexes.erase(it);
    } else {
        specialRangeIndexes[it->first + 1] = it->second;
        specialRangeIndexes.erase(it);
    }
}

void removeFromEnd(SpecialRangesIterator it,
                   std::set<int> &specialIndexes,
                   SpecialRangesType &specialRangeIndexes)
{
    if (it->second - it->first == 1) {
        specialIndexes.insert(it->first);
        specialRangeIndexes.erase(it);
    } else {
        specialRangeIndexes[it->first] = it->second - 1;
    }
}

void removeFromMiddle(int index,
                      SpecialRangesIterator it,
                      std::set<int> &specialIndexes,
                      SpecialRangesType &specialRangeIndexes)
{
    if (index - it->first == 1 && it->second - index == 1) {
        specialIndexes.insert(it->first);
        specialIndexes.insert(it->second);
        specialRangeIndexes.erase(it);
    } else {
        auto prevRange = *it;
OMP Education's avatar
OMP Education включено в состав коммита
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
        if (index - it->first == 1) {
            specialIndexes.insert(it->first);
            specialRangeIndexes[index + 1] = prevRange.second;
            auto removingIt = std::find_if(specialRangeIndexes.begin(),
                                           specialRangeIndexes.end(),
                                           [prevRange](const std::pair<int, int> &t) -> bool {
                                               return prevRange.first == t.first
                                                      && prevRange.second == t.second;
                                           });
            if (removingIt != specialRangeIndexes.end())
                specialRangeIndexes.erase(removingIt);
            return;
        }

        if (it->second - index == 1) {
            specialIndexes.insert(it->second);
            specialRangeIndexes[prevRange.first] = index - 1;
            auto removingIt = std::find_if(specialRangeIndexes.begin(),
                                           specialRangeIndexes.end(),
                                           [prevRange](const std::pair<int, int> &t) -> bool {
                                               return prevRange.first == t.first
                                                      && prevRange.second == t.second;
                                           });
            if (removingIt != specialRangeIndexes.end())
                specialRangeIndexes.erase(removingIt);
            return;
        }

OMP Education's avatar
OMP Education включено в состав коммита
77
78
79
80
81
82
83
        specialRangeIndexes[prevRange.first] = index - 1;
        specialRangeIndexes[index + 1] = prevRange.second;
    }
}

}

OMP Education's avatar
OMP Education включено в состав коммита
84
85
86
87
88
DocumentMapper::DocumentMapper(QQuickItem *parent) : QQuickItem(parent),
    m_orientation(Qt::Vertical),
    m_spacing(SPACING_DEFAULT_VALUE),
    m_contentHeight(0.0),
    m_contentWidth(0.0),
OMP Education's avatar
OMP Education включено в состав коммита
89
    m_lastPageActualSize(0.0),
OMP Education's avatar
OMP Education включено в состав коммита
90
    m_pageAspectRatio(1.0),
OMP Education's avatar
OMP Education включено в состав коммита
91
92
93
    m_reverse(false),
    m_showAll(false),
    m_specialPagesCount(0)
OMP Education's avatar
OMP Education включено в состав коммита
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
{
    connect(this, &DocumentMapper::widthChanged, this, &DocumentMapper::_mapPages);
    connect(this, &DocumentMapper::heightChanged, this, &DocumentMapper::_mapPages);
    connect(parentItem(), &QQuickItem::widthChanged, this, &DocumentMapper::_updateSize);
    connect(parentItem(), &QQuickItem::heightChanged, this, &DocumentMapper::_updateSize);
}

QSGNode *DocumentMapper::updatePaintNode(QSGNode *, QQuickItem::UpdatePaintNodeData *)
{
    return nullptr;
}

QHash<int, PagePosition> DocumentMapper::actualMap() const
{
    return m_actualPagesCoordinates;
}

QPointF DocumentMapper::pagePosition(int pageIndex) const
{
    if (pageIndex < 0 || pageIndex > m_originalPagesMap.pages.size())
        return {  };

    auto pagePosition = m_originalPagesMap.pages.at(pageIndex);
    return { static_cast<qreal>(pagePosition.startX), static_cast<qreal>(pagePosition.startY)};
}

qreal DocumentMapper::contentHeight() const
{
    return m_contentHeight;
}

qreal DocumentMapper::contentWidth() const
{
    return m_contentWidth;
}

qreal DocumentMapper::spacing() const
{
    return m_spacing;
}

qreal DocumentMapper::lastPageActualSize() const
{
    return m_lastPageActualSize;
}

void DocumentMapper::forceUpdate()
{
    _updateSize();
    _mapPages();
}

void DocumentMapper::setDocumentProvider(BaseDocument *documentProvider)
{
    if (documentProvider == nullptr)
        return;

    if (m_documentProvider == documentProvider)
        return;

    m_documentProvider = documentProvider;

    auto pageCount = m_documentProvider->count();
    m_originalPagesMap.pages.clear();
    m_originalPagesMap.pages.reserve(pageCount);

OMP Education's avatar
OMP Education включено в состав коммита
160
    if (pageCount < 1)
OMP Education's avatar
OMP Education включено в состав коммита
161
162
163
164
        m_spacing = 0;

    qreal originalPageStartX = 0.0;
    qreal originalPageStartY = 0.0;
OMP Education's avatar
OMP Education включено в состав коммита
165
166
    bool isNeedeRecalcWidth = qFuzzyCompare(static_cast<double>(m_pageAspectRatio),
                                            static_cast<double>(1.0));
OMP Education's avatar
OMP Education включено в состав коммита
167
168
169
    for (int i = 0; i < pageCount; ++i) {
        auto pageSize = m_documentProvider->pageSize(i);

OMP Education's avatar
OMP Education включено в состав коммита
170
171
        auto pageWidth = isNeedeRecalcWidth ? pageSize.width()
                                            : pageSize.height() * m_pageAspectRatio;
OMP Education's avatar
OMP Education включено в состав коммита
172
        m_originalPagesMap.pages.append({originalPageStartX,
OMP Education's avatar
OMP Education включено в состав коммита
173
174
175
176
                                         originalPageStartY,
                                         originalPageStartX + pageWidth,
                                         originalPageStartY + pageSize.height()});
        originalPageStartX += pageWidth;
OMP Education's avatar
OMP Education включено в состав коммита
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
        originalPageStartY += pageSize.height();
    }

    _mapPages();
}

void DocumentMapper::setOrientation(Qt::Orientation orientation)
{
    if (m_orientation == orientation)
        return;

    m_orientation = orientation;
    _mapPages();
}

OMP Education's avatar
OMP Education включено в состав коммита
192
193
194
void DocumentMapper::setSpecialPageIndexes(const std::map<int, int> &specialRangeIndexes,
                                           const std::set<int> &specialIndexes)
{
OMP Education's avatar
OMP Education включено в состав коммита
195
    m_specialRangeIndexes = specialRangeIndexes;
OMP Education's avatar
OMP Education включено в состав коммита
196
197
198
199
200
    m_specialIndexes = specialIndexes;

    _mapPages();
}

OMP Education's avatar
OMP Education включено в состав коммита
201
202
203
204
205
206
207
208
209
210
void DocumentMapper::setPageAspectRatio(qreal pageAspectRatio)
{
    if (qFuzzyCompare(static_cast<double>(m_pageAspectRatio), static_cast<double>(pageAspectRatio)))
        return;

    m_pageAspectRatio = pageAspectRatio;
    if (!m_originalPagesMap.pages.empty())
        _mapPages();
}

OMP Education's avatar
OMP Education включено в состав коммита
211
212
213
214
215
216
217
218
219
void DocumentMapper::setReverse(bool reverse)
{
    if (m_reverse == reverse)
        return;

    m_reverse = reverse;
    _mapPages();
}

OMP Education's avatar
OMP Education включено в состав коммита
220
221
222
223
224
225
226
227
228
229
void DocumentMapper::setSpacing(qreal spacing)
{
    if (qFuzzyCompare(m_spacing, spacing))
        return;

    m_spacing = spacing;
    if (!m_originalPagesMap.pages.empty())
        _mapPages();
}

OMP Education's avatar
OMP Education включено в состав коммита
230
231
232
233
234
235
236
237
238
void DocumentMapper::setShowAll(bool showAll)
{
    if (m_showAll == showAll)
        return;

    m_showAll = showAll;
    _mapPages();
}

OMP Education's avatar
OMP Education включено в состав коммита
239
240
241
242
243
244
245
246
247
248
249
250
void DocumentMapper::_mapPages()
{
    if (width() <= 0 || height() <= 0)
        return;

    if (m_documentProvider == nullptr)
        return;

    int pageIndex = 0;
    auto contentHeight = -1.0f;
    auto contentWidth = -1.0f;
    PagePosition position;
OMP Education's avatar
OMP Education включено в состав коммита
251
    int specialPagesCount = 0;
OMP Education's avatar
OMP Education включено в состав коммита
252

OMP Education's avatar
OMP Education включено в состав коммита
253
254
255
256
257
258
259
    auto maxPageIndex = m_originalPagesMap.pages.size() - 1;
    if (!m_originalPagesMap.pages.empty() && !m_specialIndexes.empty()
        && m_specialIndexes.find(maxPageIndex) == m_specialIndexes.end()
        && m_originalPagesMap.pages.size() < *m_specialIndexes.rbegin()) {
        m_specialIndexes.insert(maxPageIndex);
    }

OMP Education's avatar
OMP Education включено в состав коммита
260
    switch (m_orientation) {
OMP Education's avatar
OMP Education включено в состав коммита
261
    case Qt::Vertical: {
OMP Education's avatar
OMP Education включено в состав коммита
262
        for (auto &page : m_originalPagesMap.pages) {
OMP Education's avatar
OMP Education включено в состав коммита
263
264
            auto inverseIndex = m_reverse ? m_originalPagesMap.pages.size() - 1 - pageIndex : pageIndex;
            if (isHasSpecial() && !isIndexSpecal(inverseIndex)) {
OMP Education's avatar
OMP Education включено в состав коммита
265
266
267
268
                ++pageIndex;
                continue;
            }

OMP Education's avatar
OMP Education включено в состав коммита
269
            position.end += width() * page.heightToWidthRatio() + m_spacing;
OMP Education's avatar
OMP Education включено в состав коммита
270
            m_actualPagesCoordinates.insert(inverseIndex, position);
OMP Education's avatar
OMP Education включено в состав коммита
271
272
273
            m_lastPageActualSize = position.end - position.start - m_spacing;
            position.start = position.end;
            ++pageIndex;
OMP Education's avatar
OMP Education включено в состав коммита
274
            ++specialPagesCount;
OMP Education's avatar
OMP Education включено в состав коммита
275
276
277
278
279
280
281
282
        }

        contentHeight = position.end;
        contentWidth = -1;
        break;
    }
    case Qt::Horizontal : {
        for (auto &page : m_originalPagesMap.pages) {
OMP Education's avatar
OMP Education включено в состав коммита
283
284
            auto inverseIndex = m_reverse ? m_originalPagesMap.pages.size() - 1 - pageIndex : pageIndex;
            if (isHasSpecial() && !isIndexSpecal(inverseIndex)) {
OMP Education's avatar
OMP Education включено в состав коммита
285
286
287
288
                ++pageIndex;
                continue;
            }

OMP Education's avatar
OMP Education включено в состав коммита
289
290
            auto fitHeight = qMin(width() * page.heightToWidthRatio(), height());
            position.end += fitHeight / page.heightToWidthRatio() + m_spacing;
OMP Education's avatar
OMP Education включено в состав коммита
291
            m_actualPagesCoordinates.insert(inverseIndex, position);
OMP Education's avatar
OMP Education включено в состав коммита
292
293
294
            m_lastPageActualSize = position.end - position.start - m_spacing;
            position.start = position.end;
            ++pageIndex;
OMP Education's avatar
OMP Education включено в состав коммита
295
            ++specialPagesCount;
OMP Education's avatar
OMP Education включено в состав коммита
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
        }

        contentHeight = - 1;
        contentWidth = position.end;
        break;
    }
    }

    if (!qFuzzyCompare(double(contentHeight), double(m_contentHeight))) {
        m_contentHeight = contentHeight;
        emit contentHeightChanged(m_contentHeight);
    }

    if (!qFuzzyCompare(double(contentWidth), double(m_contentWidth))) {
        m_contentWidth = contentWidth;
        emit contentWidthChanged(m_contentWidth);
    }

OMP Education's avatar
OMP Education включено в состав коммита
314
315
316
317
318
    if (specialPagesCount != m_specialPagesCount) {
        m_specialPagesCount = specialPagesCount;
        emit specialPagesCountChanged(m_specialPagesCount);
    }

OMP Education's avatar
OMP Education включено в состав коммита
319
320
321
322
323
324
325
326
327
328
329
330
    emit mapEnd();
}

void DocumentMapper::_updateSize()
{
    if (parentItem() == nullptr)
        return;

    setWidth(parentItem()->width());
    setHeight(parentItem()->height());
}

OMP Education's avatar
OMP Education включено в состав коммита
331
332
bool DocumentMapper::isIndexSpecal(int pageIndex) const
{
OMP Education's avatar
OMP Education включено в состав коммита
333
334
    auto it = std::find_if(m_specialRangeIndexes.begin(),
                           m_specialRangeIndexes.end(),
OMP Education's avatar
OMP Education включено в состав коммита
335
336
337
338
                           [pageIndex](const std::pair<int, int> &t) -> bool {
                               return pageIndex >= t.first && pageIndex <= t.second;
                           });

OMP Education's avatar
OMP Education включено в состав коммита
339
340
    return m_specialIndexes.find(pageIndex) != m_specialIndexes.end()
           || it != m_specialRangeIndexes.end();
OMP Education's avatar
OMP Education включено в состав коммита
341
342
343
344
}

bool DocumentMapper::isHasSpecial() const
{
OMP Education's avatar
OMP Education включено в состав коммита
345
    return m_showAll ? false : !(m_specialRangeIndexes.empty() && m_specialIndexes.empty());
OMP Education's avatar
OMP Education включено в состав коммита
346
347
348
349
350
351
}

int DocumentMapper::minSpecialIndex() const
{
    int firstMin = m_specialIndexes.empty() ? 0 : *m_specialIndexes.begin();

OMP Education's avatar
OMP Education включено в состав коммита
352
    if (m_specialRangeIndexes.empty())
OMP Education's avatar
OMP Education включено в состав коммита
353
354
        return firstMin;

OMP Education's avatar
OMP Education включено в состав коммита
355
356
357
358
359
360
    if (!m_specialIndexes.empty())
        return std::min(firstMin,
                        std::min(m_specialRangeIndexes.begin()->first,
                                 m_originalPagesMap.pages.size() - 1));

    return std::min(m_specialRangeIndexes.begin()->first, m_originalPagesMap.pages.size() - 1);
OMP Education's avatar
OMP Education включено в состав коммита
361
362
363
364
}

int DocumentMapper::maxSpecialIndex() const
{
OMP Education's avatar
OMP Education включено в состав коммита
365
366
367
    int firstMax = m_specialIndexes.empty()
                       ? 0
                       : std::min(*m_specialIndexes.rbegin(), m_originalPagesMap.pages.size() - 1);
OMP Education's avatar
OMP Education включено в состав коммита
368

OMP Education's avatar
OMP Education включено в состав коммита
369
    if (m_specialRangeIndexes.empty())
OMP Education's avatar
OMP Education включено в состав коммита
370
371
        return firstMax;

OMP Education's avatar
OMP Education включено в состав коммита
372
    if (m_specialRangeIndexes.size() == 1)
OMP Education's avatar
OMP Education включено в состав коммита
373
        return std::max(firstMax,
OMP Education's avatar
OMP Education включено в состав коммита
374
                        std::min(m_specialRangeIndexes.begin()->second,
OMP Education's avatar
OMP Education включено в состав коммита
375
                                 m_originalPagesMap.pages.size() - 1));
OMP Education's avatar
OMP Education включено в состав коммита
376

OMP Education's avatar
OMP Education включено в состав коммита
377
378
    auto it = std::max_element(m_specialRangeIndexes.begin(),
                               m_specialRangeIndexes.end(),
OMP Education's avatar
OMP Education включено в состав коммита
379
380
381
382
                               [](const std::pair<int, int> &a, const std::pair<int, int> &b) {
                                   return a.second < b.second;
                               });

OMP Education's avatar
OMP Education включено в состав коммита
383
    return std::min(std::max(firstMax, it->second), m_originalPagesMap.pages.size() - 1);
OMP Education's avatar
OMP Education включено в состав коммита
384
385
}

OMP Education's avatar
OMP Education включено в состав коммита
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
void DocumentMapper::removeFromSpecial(int index)
{
    if (index < 0 || index > m_originalPagesMap.pages.size())
        return;

    auto iter = m_specialIndexes.find(index);
    if (iter != m_specialIndexes.end())
        m_specialIndexes.erase(iter);

    auto it = std::find_if(m_specialRangeIndexes.begin(),
                           m_specialRangeIndexes.end(),
                           [index](const std::pair<int, int> &t) -> bool {
                               return index >= t.first && index <= t.second;
                           });

    if (it == m_specialRangeIndexes.end())
        return;

    if (it->first == index) {
        removeFromBegin(it, m_specialIndexes, m_specialRangeIndexes);
        return;
    }

    if (it->second == index) {
        removeFromEnd(it, m_specialIndexes, m_specialRangeIndexes);
        return;
    }

    removeFromMiddle(index, it, m_specialIndexes, m_specialRangeIndexes);
}

void DocumentMapper::insertToSpecial(int index)
{
    if (index < 0 || index > m_originalPagesMap.pages.size())
        return;

    m_specialIndexes.insert(index);
}

OMP Education's avatar
OMP Education включено в состав коммита
425
426
427
428
429
qreal DocumentMapper::pageAspectRatio() const
{
    return m_pageAspectRatio;
}

OMP Education's avatar
OMP Education включено в состав коммита
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
std::set<int> DocumentMapper::specialIndexes() const
{
    return m_specialIndexes;
}

std::map<int, int> DocumentMapper::specialRangeIndexes() const
{
    return m_specialRangeIndexes;
}

int DocumentMapper::specialPagesCount() const
{
    return m_specialPagesCount;
}

OMP Education's avatar
OMP Education включено в состав коммита
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
PagePosition DocumentMapper::actualPagePosition(int pageIndex) const
{
    if (!m_actualPagesCoordinates.contains(pageIndex))
        return {  };

    return m_actualPagesCoordinates.value(pageIndex);
}

PageGeometry DocumentMapper::originalPageGeometry(int pageIndex) const
{
    if (pageIndex < 0 || pageIndex > m_originalPagesMap.pages.size())
        return {  };

    return m_originalPagesMap.pages.at(pageIndex);
}