File indexing completed on 2024-04-28 04:42:10

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com)
0003  * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
0004  * Copyright (C) 2014 Jarosław Staniek <staniek@kde.org>
0005  *
0006  * This library is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU Lesser General Public
0008  * License as published by the Free Software Foundation; either
0009  * version 2.1 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014  * Lesser General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Lesser General Public
0017  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "KReportDesignerSection.h"
0021 
0022 #include "KReportDesignerSectionScene.h"
0023 #include "KReportDesignerSectionView.h"
0024 #include "KReportDesigner.h"
0025 #include "KReportDesignerItemBase.h"
0026 #include "KReportUtils.h"
0027 #include "KReportPluginInterface.h"
0028 #include "KReportPluginManager.h"
0029 #include "KReportDesignerItemRectBase.h"
0030 #include "KReportDesignerItemLine.h"
0031 #include "KReportRuler_p.h"
0032 #include "KReportZoomHandler_p.h"
0033 #include "KReportUtils_p.h"
0034 #include "KReportPluginMetaData.h"
0035 #include "kreport_debug.h"
0036 
0037 #include <QLabel>
0038 #include <QFrame>
0039 #include <QDomDocument>
0040 #include <QLayout>
0041 #include <QGridLayout>
0042 #include <QMouseEvent>
0043 #include <QApplication>
0044 #include <QApplication>
0045 #include <QIcon>
0046 
0047 
0048 //! @internal
0049 class ReportResizeBar : public QFrame
0050 {
0051     Q_OBJECT
0052 public:
0053     explicit ReportResizeBar(QWidget * parent = nullptr, Qt::WindowFlags f = nullptr);
0054 
0055 Q_SIGNALS:
0056     void barDragged(int delta);
0057 
0058 protected:
0059     void mouseMoveEvent(QMouseEvent * e) override;
0060 };
0061 
0062 //! @internal
0063 class KReportDesignerSectionTitle : public QLabel
0064 {
0065     Q_OBJECT
0066 public:
0067     explicit KReportDesignerSectionTitle(QWidget *parent = nullptr);
0068     ~KReportDesignerSectionTitle() override;
0069 
0070 Q_SIGNALS:
0071     void clicked();
0072 
0073 protected:
0074     void paintEvent(QPaintEvent* event) override;
0075     void mousePressEvent(QMouseEvent *event) override;
0076 };
0077 
0078 //! @internal
0079 class Q_DECL_HIDDEN KReportDesignerSection::Private
0080 {
0081 public:
0082     explicit Private()
0083     {}
0084 
0085     ~Private()
0086     {}
0087 
0088     KReportDesignerSectionTitle *title;
0089     KReportDesignerSectionScene *scene;
0090     ReportResizeBar *resizeBar;
0091     KReportDesignerSectionView *sceneView;
0092     KReportDesigner*reportDesigner;
0093     KReportRuler *sectionRuler;
0094 
0095     KReportSectionData *sectionData;
0096     int dpiY;
0097     bool slotPropertyChangedEnabled = true;
0098 };
0099 
0100 
0101 KReportDesignerSection::KReportDesignerSection(KReportDesigner * rptdes,
0102                                                const KReportZoomHandler &zoomHandler)
0103         : QWidget(rptdes)
0104         , d(new Private())
0105 {
0106     Q_ASSERT(rptdes);
0107     d->sectionData = new KReportSectionData(this);
0108     connect(d->sectionData->propertySet(), SIGNAL(propertyChanged(KPropertySet&,KProperty&)),
0109             this, SLOT(slotPropertyChanged(KPropertySet&,KProperty&)));
0110 
0111     d->dpiY = KReportPrivate::dpiY();
0112 
0113     d->reportDesigner = rptdes;
0114     setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0115 
0116     QGridLayout * glayout = new QGridLayout(this);
0117     glayout->setSpacing(0);
0118     glayout->setMargin(0);
0119     glayout->setColumnStretch(1, 1);
0120     glayout->setRowStretch(1, 1);
0121     glayout->setSizeConstraint(QLayout::SetFixedSize);
0122 
0123     // ok create the base interface
0124     d->title = new KReportDesignerSectionTitle(this);
0125     d->title->setObjectName(QLatin1String("detail"));
0126     d->title->setText(tr("Detail"));
0127 
0128     d->sectionRuler = new KReportRuler(this, Qt::Vertical, zoomHandler);
0129     d->sectionRuler->setUnit(d->reportDesigner->pageUnit());
0130     d->scene = new KReportDesignerSectionScene(d->reportDesigner->pageWidthPx(), d->dpiY, rptdes);
0131     d->scene->setBackgroundBrush(d->sectionData->backgroundColor());
0132 
0133     d->sceneView = new KReportDesignerSectionView(rptdes, d->scene, this);
0134     d->sceneView->setObjectName(QLatin1String("scene view"));
0135     d->sceneView->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0136 
0137     d->resizeBar = new ReportResizeBar(this);
0138 
0139     connect(d->resizeBar, SIGNAL(barDragged(int)), this, SLOT(slotResizeBarDragged(int)));
0140     connect(d->reportDesigner, &KReportDesigner::pagePropertyChanged,
0141         this, &KReportDesignerSection::slotPageOptionsChanged);
0142     connect(d->scene, &KReportDesignerSectionScene::clicked, this, &KReportDesignerSection::slotSceneClicked);
0143     connect(d->scene, SIGNAL(lostFocus()), d->title, SLOT(update()));
0144     connect(d->title, &KReportDesignerSectionTitle::clicked, this, &KReportDesignerSection::slotSceneClicked);
0145 
0146     glayout->addWidget(d->title, 0, 0, 1, 2);
0147     glayout->addWidget(d->sectionRuler, 1, 0);
0148     glayout->addWidget(d->sceneView , 1, 1);
0149     glayout->addWidget(d->resizeBar, 2, 0, 1, 2);
0150     d->sectionRuler->setFixedWidth(d->sectionRuler->sizeHint().width());
0151 
0152     setLayout(glayout);
0153     slotResizeBarDragged(0);
0154 }
0155 
0156 KReportDesignerSection::~KReportDesignerSection()
0157 {
0158     delete d;
0159 }
0160 
0161 void KReportDesignerSection::setTitle(const QString & s)
0162 {
0163     d->title->setText(s);
0164 }
0165 
0166 void KReportDesignerSection::slotResizeBarDragged(int delta, bool changeSet)
0167 {
0168     if (d->sceneView->designer() && d->sceneView->designer()->propertySet()->property("page-size").value().toString() == QLatin1String("Labels")) {
0169         return; // we don't want to allow this on reports that are for labels
0170     }
0171 
0172     if (changeSet) {
0173       slotSceneClicked(); // switches property set to this section
0174     }
0175 
0176     qreal h = d->scene->height() + delta;
0177 
0178     if (h < 1) h = 1;
0179 
0180     h = d->scene->gridPoint(QPointF(0, h)).y();
0181     d->slotPropertyChangedEnabled = false; // reduce updates
0182     d->sectionData->setHeight(INCH_TO_POINT(h / d->dpiY),
0183                               delta == 0 ? KProperty::ValueOption::IgnoreOld
0184                                          : KProperty::ValueOption::None);
0185     d->slotPropertyChangedEnabled = true;
0186     d->sectionRuler->setRulerLength(h);
0187 
0188     const QRect newSceneRect(0, 0, d->scene->width(), h);
0189     if (d->scene->sceneRect() != newSceneRect) {
0190         d->scene->setSceneRect(newSceneRect);
0191     }
0192     d->sceneView->resizeContents(newSceneRect.size());
0193 
0194     if (delta != 0) {
0195         d->reportDesigner->setModified(true);
0196     }
0197 }
0198 
0199 void KReportDesignerSection::buildXML(QDomDocument *doc, QDomElement *section)
0200 {
0201     KReportUtils::setAttribute(section, QLatin1String("svg:height"), d->sectionData->height());
0202     section->setAttribute(QLatin1String("fo:background-color"), d->sectionData->backgroundColor().name());
0203 
0204     // now get a list of all the QGraphicsItems on this scene and output them.
0205     QGraphicsItemList list = d->scene->items();
0206     for (QGraphicsItemList::iterator it = list.begin();
0207             it != list.end(); ++it) {
0208         KReportDesignerItemBase::buildXML((*it), doc, section);
0209     }
0210 }
0211 
0212 void KReportDesignerSection::initFromXML(const QDomNode & section)
0213 {
0214     QDomNodeList nl = section.childNodes();
0215     QDomNode node;
0216     QString n;
0217 
0218     qreal ptHeight = KReportUtils::readSizeAttributes(section.toElement(), QSizeF(DEFAULT_SECTION_SIZE_PT, DEFAULT_SECTION_SIZE_PT)).height();
0219     d->sectionData->setHeight(ptHeight);
0220 
0221     qreal h  = POINT_TO_INCH(ptHeight) * d->dpiY;
0222     //kreportDebug() << "Section Height: " << h;
0223     d->scene->setSceneRect(0, 0, d->scene->width(), h);
0224     slotResizeBarDragged(0);
0225     d->sectionData->setBackgroundColor(KReportUtils::attr(
0226         section.toElement(), QLatin1String("fo:background-color"), QColor(Qt::white)));
0227     d->sectionData->propertySet()->clearModifiedFlags();
0228 
0229     KReportPluginManager* manager = KReportPluginManager::self();
0230     for (int i = 0; i < nl.count(); ++i) {
0231         node = nl.item(i);
0232         n = node.nodeName();
0233         QObject *obj = nullptr;
0234         if (n.startsWith(QLatin1String("report:"))) {
0235             //Load objects
0236             //report:line is a special case as it is not a plugin
0237             KReportPluginInterface *plugin = nullptr;
0238             QString reportItemName = n.mid(qstrlen("report:"));
0239             if (reportItemName == QLatin1String("line")) {
0240                 obj = new KReportDesignerItemLine(node, d->sceneView->designer(), d->scene);
0241             } else {
0242                 plugin = manager->plugin(reportItemName);
0243                 if (plugin) {
0244                     obj = plugin->createDesignerInstance(node, d->reportDesigner, d->scene);
0245                 }
0246             }
0247             if (obj) {
0248                 KReportDesignerItemRectBase *entity = dynamic_cast<KReportDesignerItemRectBase*>(obj);
0249                 if (entity) {
0250                     entity->setVisible(true);
0251                 }
0252                 KReportItemBase *item = dynamic_cast<KReportItemBase*>(obj);
0253                 if (item) {
0254                     item->setUnit(d->reportDesigner->pageUnit());
0255                     if (plugin) {
0256                         KReportDesigner::addMetaProperties(item->propertySet(),
0257                                                            plugin->metaData()->name(),
0258                                                            plugin->metaData()->iconName());
0259                     }
0260                     item->propertySet()->clearModifiedFlags();
0261                 }
0262             }
0263         }
0264         if (!obj) {
0265             kreportWarning() << "Unknown element" << n << "while parsing section"
0266                              << section.toElement().tagName();
0267         }
0268     }
0269 }
0270 
0271 QSize KReportDesignerSection::sizeHint() const
0272 {
0273     return QSize(d->scene->width()  + d->sectionRuler->frameSize().width(), d->title->frameSize().height() + d->sceneView->sizeHint().height() + d->resizeBar->frameSize().height());
0274 }
0275 
0276 void KReportDesignerSection::slotPageOptionsChanged(KPropertySet &set)
0277 {
0278     Q_UNUSED(set)
0279 
0280     KReportUnit unit = d->reportDesigner->pageUnit();
0281     d->sectionData->setUnit(unit);
0282 
0283     //update items position with unit
0284     QList<QGraphicsItem*> itms = d->scene->items();
0285     for (int i = 0; i < itms.size(); ++i) {
0286         KReportItemBase *obj = dynamic_cast<KReportItemBase*>(itms[i]);
0287         if (obj) {
0288             obj->setUnit(unit);
0289         }
0290     }
0291 
0292     d->scene->setSceneRect(0, 0, d->reportDesigner->pageWidthPx(), d->scene->height());
0293     d->title->setMinimumWidth(d->reportDesigner->pageWidthPx() + d->sectionRuler->frameSize().width());
0294     d->sectionRuler->setUnit(d->reportDesigner->pageUnit());
0295 
0296     //Trigger a redraw of the background
0297     d->sceneView->resizeContents(QSize(d->scene->width(), d->scene->height()));
0298 
0299     d->sceneView->resetCachedContent();
0300     d->sceneView->update();
0301 
0302     d->reportDesigner->adjustSize();
0303     d->reportDesigner->repaint();
0304 }
0305 
0306 void KReportDesignerSection::slotSceneClicked()
0307 {
0308     d->reportDesigner->setActiveScene(d->scene);
0309     d->reportDesigner->changeSet(d->sectionData->propertySet());
0310 }
0311 
0312 void KReportDesignerSection::slotPropertyChanged(KPropertySet &s, KProperty &p)
0313 {
0314     Q_UNUSED(s)
0315     if (!d->slotPropertyChangedEnabled) {
0316         return;
0317     }
0318     //kreportDebug() << p.name();
0319 
0320     //Handle Background Color
0321     if (p.name() == "background-color") {
0322         d->scene->setBackgroundBrush(p.value().value<QColor>());
0323     }
0324 
0325     if (p.name() == "height") {
0326         const QRect newSceneRect(0, 0, d->scene->width(), POINT_TO_INCH(d->sectionData->height()) * d->dpiY);
0327         if (d->scene->sceneRect() != newSceneRect) {
0328             d->scene->setSceneRect(newSceneRect);
0329         }
0330         d->sceneView->resizeContents(newSceneRect.size());
0331     }
0332 
0333     if (d->reportDesigner)
0334         d->reportDesigner->setModified(true);
0335 
0336     d->sceneView->resetCachedContent();
0337     d->scene->update();
0338 }
0339 
0340 void KReportDesignerSection::setSectionCursor(const QCursor& c)
0341 {
0342     if (d->sceneView)
0343         d->sceneView->setCursor(c);
0344 }
0345 
0346 void KReportDesignerSection::unsetSectionCursor()
0347 {
0348     if (d->sceneView)
0349         d->sceneView->unsetCursor();
0350 }
0351 
0352 QGraphicsItemList KReportDesignerSection::items() const
0353 {
0354     QGraphicsItemList items;
0355 
0356     if (d->scene) {
0357         foreach (QGraphicsItem *itm, d->scene->items()) {
0358             if (itm->parentItem() == nullptr) {
0359                 items << itm;
0360             }
0361         }
0362     }
0363 
0364     return items;
0365 }
0366 
0367 
0368 //
0369 // class ReportResizeBar
0370 //
0371 ReportResizeBar::ReportResizeBar(QWidget * parent, Qt::WindowFlags f)
0372         : QFrame(parent, f)
0373 {
0374     setCursor(QCursor(Qt::SizeVerCursor));
0375     setFrameStyle(QFrame::HLine);
0376     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
0377 }
0378 
0379 void ReportResizeBar::mouseMoveEvent(QMouseEvent * e)
0380 {
0381     e->accept();
0382     emit barDragged(e->y());
0383 }
0384 
0385 //=============================================================================
0386 
0387 KReportDesignerSectionTitle::KReportDesignerSectionTitle(QWidget*parent) : QLabel(parent)
0388 {
0389     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
0390     setAlignment(Qt::AlignLeft | Qt::AlignTop);
0391     setMinimumHeight(qMax(fontMetrics().lineSpacing(),16 + 2)); //16 = Small icon size
0392 }
0393 
0394 KReportDesignerSectionTitle::~KReportDesignerSectionTitle()
0395 {
0396 }
0397 
0398 //! \return true if \a o has parent \a par.
0399 static bool hasParent(QObject* par, QObject* o)
0400 {
0401     if (!o || !par)
0402         return false;
0403     while (o && o != par)
0404         o = o->parent();
0405     return o == par;
0406 }
0407 
0408 static void replaceColors(QPixmap* original, const QColor& color)
0409 {
0410     QImage dest(original->toImage());
0411     QPainter p(&dest);
0412     p.setCompositionMode(QPainter::CompositionMode_SourceIn);
0413     p.fillRect(dest.rect(), color);
0414     *original = QPixmap::fromImage(dest);
0415 }
0416 
0417 void KReportDesignerSectionTitle::paintEvent(QPaintEvent * event)
0418 {
0419     QPainter painter(this);
0420     KReportDesignerSection* section = dynamic_cast<KReportDesignerSection*>(parent());
0421     if (section) {
0422         const bool current = section->d->scene == section->d->reportDesigner->activeScene();
0423         QPalette::ColorGroup cg = QPalette::Inactive;
0424         QWidget *activeWindow = QApplication::activeWindow();
0425         if (activeWindow) {
0426             QWidget *par = activeWindow->focusWidget();
0427             if (qobject_cast<KReportDesignerSectionView*>(par)) {
0428                 par = par->parentWidget(); // we're close, pick common parent
0429             }
0430             if (hasParent(par, this)) {
0431                 cg = QPalette::Active;
0432             }
0433         }
0434         if (current) {
0435             painter.fillRect(rect(), palette().brush(cg, QPalette::Highlight));
0436         }
0437         painter.setPen(palette().color(cg, current ? QPalette::HighlightedText : QPalette::WindowText));
0438         QPixmap pixmap(QIcon::fromTheme(QLatin1String("arrow-down")).pixmap(16,16));
0439         replaceColors(&pixmap, painter.pen().color());
0440         const int left = 25;
0441         painter.drawPixmap(QPoint(left, (height() - pixmap.height()) / 2), pixmap);
0442 
0443         painter.drawText(rect().adjusted(left + pixmap.width() + 4, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, text());
0444     }
0445     QFrame::paintEvent(event);
0446 }
0447 
0448 void KReportDesignerSectionTitle::mousePressEvent(QMouseEvent *event)
0449 {
0450     QLabel::mousePressEvent(event);
0451     if (event->button() == Qt::LeftButton) {
0452         emit clicked();
0453     }
0454 }
0455 
0456 #include "KReportDesignerSection.moc"