File indexing completed on 2024-05-05 12:57:28

0001 /*
0002  * Copyright (C) 2010-2015 by Stephen Allewell
0003  * steve.allewell@gmail.com
0004  *
0005  * This program is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation; either version 2 of the License, or
0008  * (at your option) any later version.
0009  */
0010 
0011 #include "PageLayoutEditor.h"
0012 
0013 #include <QMenu>
0014 #include <QMouseEvent>
0015 #include <QPainter>
0016 #include <QPointer>
0017 #include <QRubberBand>
0018 #include <QToolTip>
0019 
0020 #include <KLocalizedString>
0021 
0022 #include <math.h>
0023 
0024 #include "Document.h"
0025 #include "Element.h"
0026 #include "KeyElementDlg.h"
0027 #include "Page.h"
0028 #include "PagePreviewListWidgetItem.h"
0029 #include "PagePropertiesDlg.h"
0030 #include "TextElementDlg.h"
0031 #include "configuration.h"
0032 
0033 PageLayoutEditor::PageLayoutEditor(QWidget *parent, Document *document)
0034     : QWidget(parent)
0035     , m_document(document)
0036     , m_pagePreview(nullptr)
0037     , m_elementUnderCursor(nullptr)
0038     , m_selecting(false)
0039     , m_resizing(false)
0040     , m_moved(false)
0041     , m_node(nullptr)
0042     , m_showGrid(Configuration::page_ShowGrid())
0043     , m_gridSize(Configuration::page_GridSize())
0044     , m_zoomFactor(1.0)
0045 {
0046     setContextMenuPolicy(Qt::CustomContextMenu);
0047     setPagePreview(nullptr);
0048     setMouseTracking(true);
0049 
0050     connect(this, &PageLayoutEditor::customContextMenuRequested, this, &PageLayoutEditor::contextMenuRequestedOn);
0051 }
0052 
0053 double PageLayoutEditor::zoomFactor() const
0054 {
0055     return m_zoomFactor;
0056 }
0057 
0058 int PageLayoutEditor::gridSize() const
0059 {
0060     return m_gridSize;
0061 }
0062 
0063 bool PageLayoutEditor::showGrid() const
0064 {
0065     return m_showGrid;
0066 }
0067 
0068 void PageLayoutEditor::setPagePreview(PagePreviewListWidgetItem *pagePreview)
0069 {
0070     if ((m_pagePreview = pagePreview)) {
0071         m_boundary.setElement(nullptr);
0072         show();
0073         updatePagePreview();
0074     } else {
0075         hide();
0076     }
0077 }
0078 
0079 void PageLayoutEditor::updatePagePreview()
0080 {
0081     if (m_pagePreview) {
0082         m_paperWidth = m_pagePreview->paperWidth();
0083         m_paperHeight = m_pagePreview->paperHeight();
0084         setMinimumSize(scale(m_paperWidth), scale(m_paperHeight));
0085         resize(minimumSize());
0086         update();
0087     }
0088 }
0089 
0090 void PageLayoutEditor::setZoomFactor(double zoomFactor)
0091 {
0092     m_zoomFactor = zoomFactor;
0093 
0094     if (m_pagePreview) {
0095         setMinimumSize(scale(m_paperWidth), scale(m_paperHeight));
0096         resize(minimumSize());
0097     }
0098 
0099     repaint();
0100 }
0101 
0102 void PageLayoutEditor::setGridSize(int size)
0103 {
0104     m_gridSize = size;
0105     repaint();
0106 }
0107 
0108 void PageLayoutEditor::setShowGrid(bool show)
0109 {
0110     m_showGrid = show;
0111     repaint();
0112 }
0113 
0114 void PageLayoutEditor::setSelecting(bool selecting)
0115 {
0116     m_selecting = selecting;
0117 
0118     if (!selecting) {
0119         m_boundary.setElement(nullptr);
0120     }
0121 }
0122 
0123 void PageLayoutEditor::mousePressEvent(QMouseEvent *event)
0124 {
0125     m_moved = false;
0126     m_start = m_end = toSnap(event->pos());
0127 
0128     if (event->buttons() & Qt::LeftButton) {
0129         if (m_selecting) {
0130             if (!m_node) {
0131                 m_boundary.setElement(m_pagePreview->page()->itemAt(unscale(event->pos())));
0132             }
0133         } else {
0134             m_rubberBand = QRect(m_start, m_end).normalized();
0135         }
0136 
0137         update();
0138     }
0139 }
0140 
0141 void PageLayoutEditor::mouseMoveEvent(QMouseEvent *event)
0142 {
0143     m_end = toSnap(event->pos());
0144 
0145     if (event->buttons() & Qt::LeftButton) {
0146         if (m_selecting) {
0147             QPoint offset = m_end - m_start;
0148 
0149             if (offset != QPoint(0, 0) && m_boundary.element()) {
0150                 if (m_node) {
0151                     m_boundary.moveNode(m_node, m_end);
0152                 } else {
0153                     m_boundary.element()->move(offset);
0154                     m_boundary.setRectangle(m_boundary.rectangle().translated(offset));
0155                 }
0156 
0157                 QToolTip::showText(QCursor::pos(),
0158                                    QString::fromLatin1("%1,%2 %3 x %4")
0159                                        .arg(m_boundary.rectangle().left())
0160                                        .arg(m_boundary.rectangle().top())
0161                                        .arg(m_boundary.rectangle().width())
0162                                        .arg(m_boundary.rectangle().height()));
0163                 m_moved = true;
0164                 m_start = m_end;
0165                 update();
0166             }
0167         } else {
0168             m_rubberBand = QRect(m_start, m_end).normalized();
0169             update();
0170 
0171             QToolTip::showText(
0172                 QCursor::pos(),
0173                 QString::fromLatin1("%1,%2 %3 x %4").arg(m_rubberBand.left()).arg(m_rubberBand.top()).arg(m_rubberBand.width()).arg(m_rubberBand.height()));
0174         }
0175     } else {
0176         if (m_boundary.element() && (m_node = m_boundary.node(unscale(event->pos())))) {
0177             setCursor(m_boundary.cursor(m_node));
0178         } else {
0179             setCursor(Qt::ArrowCursor);
0180         }
0181     }
0182 }
0183 
0184 void PageLayoutEditor::mouseReleaseEvent(QMouseEvent *event)
0185 {
0186     if (!m_selecting) {
0187         if (m_rubberBand.isValid()) {
0188             m_end = toSnap(event->pos());
0189             QRect selection = QRect(m_start, m_end).normalized();
0190             m_rubberBand = QRect();
0191 
0192             if (selection.isValid()) {
0193                 emit selectionMade(selection.adjusted(0, 0, -1, -1));
0194             }
0195         }
0196     } else if (m_boundary.element() && m_moved) {
0197         emit elementGeometryChanged();
0198     }
0199 
0200     m_moved = false;
0201     update();
0202 }
0203 
0204 void PageLayoutEditor::paintEvent(QPaintEvent *event)
0205 {
0206     QPainter painter;
0207     QRect updateRect = event->rect(); // in device coordinates
0208 
0209     painter.begin(this);
0210     painter.fillRect(updateRect, Qt::white);
0211     painter.setRenderHint(QPainter::Antialiasing, true);
0212     painter.setWindow(0, 0, m_paperWidth, m_paperHeight);
0213 
0214     m_pagePreview->page()->render(m_document, &painter);
0215 
0216     // draw snap grid
0217     if (m_showGrid) {
0218         int xOffset = (m_paperWidth / 2) % m_gridSize;
0219         int yOffset = (m_paperHeight / 2) % m_gridSize;
0220 
0221         for (int y = yOffset; y < m_paperHeight; y += m_gridSize) {
0222             for (int x = xOffset; x < m_paperWidth; x += m_gridSize) {
0223                 painter.setPen(QPen(Qt::black, 0.2));
0224                 painter.drawPoint(x, y);
0225             }
0226         }
0227     }
0228 
0229     if (m_rubberBand.isValid()) {
0230         bool qt4CompatiblePainting = painter.renderHints() & QPainter::Qt4CompatiblePainting;
0231         painter.setRenderHint(QPainter::Qt4CompatiblePainting, true);
0232         QStyleOptionRubberBand opt;
0233         opt.initFrom(this);
0234         opt.shape = QRubberBand::Rectangle;
0235         opt.opaque = false;
0236         opt.rect = m_rubberBand;
0237         style()->drawControl(QStyle::CE_RubberBand, &opt, &painter);
0238         painter.setRenderHint(QPainter::Qt4CompatiblePainting, qt4CompatiblePainting);
0239     }
0240 
0241     if (m_boundary.isValid()) {
0242         if (m_pagePreview->page()->elements().contains(m_boundary.element())) {
0243             m_boundary.render(&painter);
0244         } else {
0245             m_boundary.setElement(nullptr);
0246         }
0247     }
0248 
0249     painter.end();
0250 }
0251 
0252 void PageLayoutEditor::contextMenuRequestedOn(const QPoint &pos)
0253 {
0254     Element *element = m_pagePreview->page()->itemAt(unscale(pos));
0255 
0256     if (element) {
0257         if (m_boundary.element() != element) {
0258             m_boundary.setElement(element);
0259         }
0260     } else {
0261         m_boundary.setElement(nullptr);
0262     }
0263 
0264     update();
0265 }
0266 
0267 QPoint PageLayoutEditor::toSnap(const QPoint &pos) const
0268 {
0269     int scaledGridSize = scale(m_gridSize);
0270     int xOffset = (width() / 2) % scaledGridSize;
0271     int yOffset = (height() / 2) % scaledGridSize;
0272     int xSnap = unscale((((pos.x() - xOffset + 5) / scaledGridSize) * scaledGridSize) + xOffset);
0273     int ySnap = unscale((((pos.y() - yOffset + 5) / scaledGridSize) * scaledGridSize) + yOffset);
0274     return QPoint(xSnap, ySnap);
0275 }
0276 
0277 int PageLayoutEditor::scale(int n) const
0278 {
0279     return int(double(n) * m_zoomFactor);
0280 }
0281 
0282 int PageLayoutEditor::unscale(int n) const
0283 {
0284     return int(double(n) / m_zoomFactor);
0285 }
0286 
0287 QPoint PageLayoutEditor::scale(const QPoint &p) const
0288 {
0289     return QPoint(scale(p.x()), scale(p.y()));
0290 }
0291 
0292 QPoint PageLayoutEditor::unscale(const QPoint &p) const
0293 {
0294     return QPoint(unscale(p.x()), unscale(p.y()));
0295 }
0296 
0297 QRect PageLayoutEditor::scale(const QRect &r) const
0298 {
0299     return QRect(scale(r.topLeft()), scale(r.bottomRight()));
0300 }
0301 
0302 QRect PageLayoutEditor::unscale(const QRect &r) const
0303 {
0304     return QRect(unscale(r.topLeft()), unscale(r.bottomRight()));
0305 }
0306 
0307 #include "moc_PageLayoutEditor.cpp"