1 /* This file is part of the KDE project
2  * Copyright (C) 2001-2007 by OpenMFG, LLC <info@openmfg.com>
3  * Copyright (C) 2007-2010 by Adam Pigg <adam@piggz.co.uk>
4  * Copyright (C) 2011-2017 Jarosław Staniek <staniek@kde.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "KReportDesigner.h"
21 #include "KReportDesign_p.h"
22 #include "KReportDesignerItemLine.h"
23 #include "KReportDesignerSection.h"
24 #include "KReportDesignerSectionDetail.h"
25 #include "KReportDesignerSectionDetailGroup.h"
26 #include "KReportDesignerSectionScene.h"
27 #include "KReportDesignerSectionView.h"
28 #include "KReportPageSize.h"
29 #include "KReportPluginInterface.h"
30 #include "KReportPluginManager.h"
31 #include "KReportPluginMetaData.h"
32 #include "KReportPropertiesButton.h"
33 #include "KReportRuler_p.h"
34 #include "KReportSection.h"
35 #include "KReportSectionEditor.h"
36 #include "KReportUtils.h"
37 #include "KReportUtils_p.h"
38 #include "KReportZoomHandler_p.h"
39 #include "kreport_debug.h"
40 #ifdef KREPORT_SCRIPTING
41 #include "KReportScriptSource.h"
42 #endif
43 
44 #include <KPropertyListData>
45 
46 #include <KStandardShortcut>
47 #include <KStandardGuiItem>
48 #include <QLayout>
49 #include <QDomDocument>
50 #include <QVBoxLayout>
51 #include <QGridLayout>
52 #include <QGraphicsSceneMouseEvent>
53 #include <QMenu>
54 #include <QPointer>
55 #include <QIcon>
56 #include <QAction>
57 #include <QMouseEvent>
58 #include <QMessageBox>
59 
60 //! Also add public method for runtime?
61 const char ns[] = "http://kexi-project.org/report/2.0";
62 
propertyToElement(QDomDocument * d,KProperty * p)63 static QDomElement propertyToElement(QDomDocument* d, KProperty* p)
64 {
65     QDomElement e = d->createElement(QLatin1String("report:" + p->name().toLower()));
66     e.appendChild(d->createTextNode(p->value().toString()));
67     return e;
68 }
69 
70 //
71 // define and implement the ReportWriterSectionData class
72 // a simple class to hold/hide data in the ReportHandler class
73 //
74 class ReportWriterSectionData
75 {
76 public:
ReportWriterSectionData()77     ReportWriterSectionData() {
78         selected_x_offset = 0;
79         selected_y_offset = 0;
80         mouseAction = MouseAction::None;
81     }
~ReportWriterSectionData()82     virtual ~ReportWriterSectionData() {
83     }
84 
85     enum class MouseAction {
86         None = 0,
87         Insert = 1,
88         Grab = 2,
89         MoveStartPoint,
90         MoveEndPoint,
91         ResizeNW = 8,
92         ResizeN,
93         ResizeNE,
94         ResizeE,
95         ResizeSE,
96         ResizeS,
97         ResizeSW,
98         ResizeW
99     };
100 
101     int selected_x_offset;
102     int selected_y_offset;
103 
104     MouseAction mouseAction;
105     QString itemToInsert;
106 
107     QList<KReportDesignerItemBase*> copy_list;
108     QList<KReportDesignerItemBase*> cut_list;
109 };
110 
111 //! @internal
112 class Q_DECL_HIDDEN KReportDesigner::Private
113 {
114 public:
115     explicit Private(KReportDesigner *designer);
116 
~Private()117     ~Private()
118     {
119         delete dataSource;
120     }
121 
122     void init(const QDomElement *xml);
123 
updateCurrentUnit()124     void updateCurrentUnit() {
125         QString u = unit->value().toString();
126         KReportUnit newUnit = KReportUnit(KReportUnit::symbolToType(u));
127         if (newUnit.isValid()) {
128             currentUnit = newUnit;
129         } else {
130             currentUnit = DEFAULT_UNIT;
131         }
132 
133         if (u == QLatin1String("dm")) {
134             gridDivisions->setOption("max", 100);
135         } else {
136             gridDivisions->setOption("max", 10);
137             if (gridDivisions->value().toInt() > 10) {
138                 gridDivisions->setValue(10, KProperty::ValueOption::IgnoreOld);
139             }
140         }
141     }
142 
143 #ifdef KREPORT_SCRIPTING
144     void updateScripts();
145 #endif
146 
147     KReportDesigner * const q;
148 
149     QGridLayout *grid;
150     KReportRuler *hruler;
151     KReportZoomHandler zoomHandler;
152     QVBoxLayout *vboxlayout;
153     KReportPropertiesButton *pageButton;
154 
155     QGraphicsScene *activeScene = nullptr;
156 
157     ReportWriterSectionData sectionData;
158 
159     KReportDesignerSection *reportHeader = nullptr;
160     KReportDesignerSection *pageHeaderFirst = nullptr;
161     KReportDesignerSection *pageHeaderOdd = nullptr;
162     KReportDesignerSection *pageHeaderEven = nullptr;
163     KReportDesignerSection *pageHeaderLast = nullptr;
164     KReportDesignerSection *pageHeaderAny = nullptr;
165 
166     KReportDesignerSection *pageFooterFirst = nullptr;
167     KReportDesignerSection *pageFooterOdd = nullptr;
168     KReportDesignerSection *pageFooterEven = nullptr;
169     KReportDesignerSection *pageFooterLast = nullptr;
170     KReportDesignerSection *pageFooterAny = nullptr;
171     KReportDesignerSection *reportFooter = nullptr;
172     KReportDesignerSectionDetail *detail = nullptr;
173 
174     //Properties
175     KPropertySet set;
176     KPropertySet *itemSet;
177     KProperty *title;
178     KProperty *pageSize;
179     KProperty *orientation;
180     KProperty *unit;
181     KProperty *customPageSize;
182     KProperty *leftMargin;
183     KProperty *rightMargin;
184     KProperty *topMargin;
185     KProperty *bottomMargin;
186     KProperty *showGrid;
187     KProperty *gridDivisions;
188     KProperty *gridSnap;
189     KProperty *labelType;
190 #ifdef KREPORT_SCRIPTING
191     KProperty *script;
192 #endif
193 
194     KReportUnit currentUnit;
195 
196     //Actions
197     QAction *editCutAction;
198     QAction *editCopyAction;
199     QAction *editPasteAction;
200     QAction *editDeleteAction;
201     QAction *sectionEdit;
202     QAction *parameterEdit;
203     QAction *itemRaiseAction;
204     QAction *itemLowerAction;
205 
206     qreal pressX = -1;
207     qreal pressY = -1;
208     qreal releaseX = -1;
209     qreal releaseY = -1;
210 
211     bool modified = false; // true if this document has been modified, false otherwise
212 
213     QString originalInterpreter; //Value of the script interpreter at load time
214     QString originalScript; //Value of the script at load time
215 
216     KReportDataSource *dataSource = nullptr;
217 #ifdef KREPORT_SCRIPTING
218     KReportScriptSource *scriptSource = nullptr;
219 #endif
220 
221 private:
222     void loadXml(const QDomElement &data);
223 };
224 
Private(KReportDesigner * designer)225 KReportDesigner::Private::Private(KReportDesigner *designer)
226     : q(designer), currentUnit(DEFAULT_UNIT_TYPE)
227 {
228 }
229 
230 // (must be init() instead of ctor because we are indirectly depending on initialized KReportDesigner::d here)
init(const QDomElement * xml)231 void KReportDesigner::Private::init(const QDomElement *xml)
232 {
233     KReportPluginManager::self(); // this loads icons early enough
234 
235     q->createProperties();
236     q->createActions();
237 
238     grid = new QGridLayout(q);
239     grid->setSpacing(0);
240     grid->setMargin(0);
241     grid->setColumnStretch(1, 1);
242     grid->setRowStretch(1, 1);
243     grid->setSizeConstraint(QLayout::SetFixedSize);
244 
245     vboxlayout = new QVBoxLayout();
246     vboxlayout->setSpacing(0);
247     vboxlayout->setMargin(0);
248     vboxlayout->setSizeConstraint(QLayout::SetFixedSize);
249 
250     //Create nice rulers
251     hruler = new KReportRuler(nullptr, Qt::Horizontal, zoomHandler);
252     hruler->setUnit(DEFAULT_UNIT);
253 
254     pageButton = new KReportPropertiesButton;
255 
256     grid->addWidget(pageButton, 0, 0);
257     grid->addWidget(hruler, 0, 1);
258     grid->addLayout(vboxlayout, 1, 0, 1, 2);
259 
260     pageButton->setMaximumSize(QSize(19, 22));
261     pageButton->setMinimumSize(QSize(19, 22));
262 
263     if (!xml) {
264         detail = new KReportDesignerSectionDetail(q);
265         vboxlayout->insertWidget(0, detail);
266     }
267 
268     connect(pageButton, &KReportPropertiesButton::released,
269             q, &KReportDesigner::slotPageButton_Pressed);
270     emit q->pagePropertyChanged(set);
271 
272     connect(&set, &KPropertySet::propertyChanged, q, &KReportDesigner::slotPropertyChanged);
273 
274     if (xml) {
275         loadXml(*xml);
276     }
277     set.clearModifiedFlags();
278     q->changeSet(&set);
279 }
280 
loadXml(const QDomElement & data)281 void KReportDesigner::Private::loadXml(const QDomElement &data)
282 {
283     if (data.tagName() != QLatin1String("report:content")) {
284         // arg we got an xml file but not one i know of
285         kreportWarning() << "root element was not <report:content>";
286     }
287     //kreportDebug() << data.text();
288 
289     QDomNodeList nlist = data.childNodes();
290     QDomNode it;
291 
292     for (int i = 0; i < nlist.count(); ++i) {
293         it = nlist.item(i);
294         // at this level all the children we get should be Elements
295         if (it.isElement()) {
296             QString n = it.nodeName().toLower();
297             //kreportDebug() << n;
298             if (n == QLatin1String("report:title")) {
299                 q->setReportTitle(it.firstChild().nodeValue());
300 #ifdef KREPORT_SCRIPTING
301             } else if (n == QLatin1String("report:script")) {
302                 originalInterpreter = it.toElement().attribute(QLatin1String("report:script-interpreter"), QLatin1String("javascript"));
303                 if (originalInterpreter.isEmpty()) {
304                     originalInterpreter = QLatin1String("javascript");
305                 }
306                 originalScript = it.firstChild().nodeValue();
307                 script->setValue(originalScript);
308 
309                 if (originalInterpreter != QLatin1String("javascript") && originalInterpreter != QLatin1String("qtscript")) {
310                     QString msg = tr("This report contains scripts of type \"%1\". "
311                                      "Only scripts written in JavaScript language are "
312                                      "supported. To prevent losing the scripts, their type "
313                                      "and content will not be changed unless you change these scripts."
314                                      ).arg(originalInterpreter);
315                     QMessageBox::warning(q, tr("Unsupported Script Type"), msg);
316                 }
317 #endif
318             } else if (n == QLatin1String("report:grid")) {
319                 showGrid->setValue(it.toElement().attribute(QLatin1String("report:grid-visible"), QString::number(1)).toInt() != 0);
320                 gridSnap->setValue(it.toElement().attribute(QLatin1String("report:grid-snap"), QString::number(1)).toInt() != 0);
321                 gridDivisions->setValue(it.toElement().attribute(QLatin1String("report:grid-divisions"), QString::number(4)).toInt());
322                 unit->setValue(it.toElement().attribute(QLatin1String("report:page-unit"), DEFAULT_UNIT_STRING));
323                 updateCurrentUnit();
324             }
325 
326             //! @todo Load page options
327             else if (n == QLatin1String("report:page-style")) {
328                 QString pagetype = it.firstChild().nodeValue();
329 
330                 if (pagetype == QLatin1String("predefined")) {
331                     pageSize->setValue(it.toElement().attribute(QLatin1String("report:page-size"), QLatin1String("A4")));
332                 } else if (pagetype == QLatin1String("custom")) {
333                     pageSize->setValue(QLatin1String("Custom"));
334                     customPageSize->setValue(QSizeF(KReportUnit::parseValue(it.toElement().attribute(QLatin1String("report:custom-page-width"), QLatin1String(""))),
335                         KReportUnit::parseValue(it.toElement().attribute(QLatin1String("report:custom-page-height"), QLatin1String("")))));
336                 } else if (pagetype == QLatin1String("label")) {
337                     //! @todo
338                 }
339 
340                 rightMargin->setValue(currentUnit.convertFromPoint(
341                     KReportUnit::parseValue(it.toElement().attribute(
342                         QLatin1String("fo:margin-right"), DEFAULT_PAGE_MARGIN_STRING))));
343                 leftMargin->setValue(currentUnit.convertFromPoint(
344                     KReportUnit::parseValue(it.toElement().attribute(
345                         QLatin1String("fo:margin-left"), DEFAULT_PAGE_MARGIN_STRING))));
346                 topMargin->setValue(currentUnit.convertFromPoint(
347                     KReportUnit::parseValue(it.toElement().attribute(
348                         QLatin1String("fo:margin-top"), DEFAULT_PAGE_MARGIN_STRING))));
349                 bottomMargin->setValue(currentUnit.convertFromPoint(
350                     KReportUnit::parseValue(it.toElement().attribute(
351                         QLatin1String("fo:margin-bottom"), DEFAULT_PAGE_MARGIN_STRING))));
352                 orientation->setValue(
353                     it.toElement().attribute(QLatin1String("report:print-orientation"),
354                                              QLatin1String("portrait")));
355             } else if (n == QLatin1String("report:body")) {
356                 QDomNodeList sectionlist = it.childNodes();
357                 QDomNode sec;
358 
359                 for (int s = 0; s < sectionlist.count(); ++s) {
360                     sec = sectionlist.item(s);
361                     if (sec.isElement()) {
362                         QString sn = sec.nodeName().toLower();
363                         //kreportDebug() << sn;
364                         if (sn == QLatin1String("report:section")) {
365                             const QString sectiontype = KReportUtils::readSectionTypeNameAttribute(sec.toElement());
366                             if (q->section(KReportSectionData::sectionTypeFromString(sectiontype)) == nullptr) {
367                                 q->insertSection(KReportSectionData::sectionTypeFromString(sectiontype));
368                                 q->section(KReportSectionData::sectionTypeFromString(sectiontype))->initFromXML(sec);
369                             }
370                         } else if (sn == QLatin1String("report:detail")) {
371                             KReportDesignerSectionDetail * rsd = new KReportDesignerSectionDetail(q);
372                             rsd->initFromXML(&sec);
373                             q->setDetail(rsd);
374                         }
375                     } else {
376                         kreportWarning() << "Encountered an unknown Element: "  << n;
377                     }
378                 }
379             }
380         } else {
381             kreportWarning() << "Encountered a child node of root that is not an Element";
382         }
383     }
384     updateScripts();
385     emit q->reportDataChanged();
386     q->slotPropertyChanged(set, *unit); // set unit for all items
387     q->setModified(false);
388 }
389 
390 #ifdef KREPORT_SCRIPTING
updateScripts()391 void KReportDesigner::Private::updateScripts()
392 {
393     if (scriptSource) {
394         QStringList sl = scriptSource->scriptList();
395         sl.prepend(QString()); // prepend "none"
396         script->setListData(sl, sl);
397     }
398 }
399 #endif
400 
401 // ----
402 
KReportDesigner(QWidget * parent)403 KReportDesigner::KReportDesigner(QWidget * parent)
404         : QWidget(parent), d(new Private(this))
405 {
406     d->init(nullptr);
407 }
408 
KReportDesigner(QWidget * parent,const QDomElement & data)409 KReportDesigner::KReportDesigner(QWidget *parent, const QDomElement &data)
410     : QWidget(parent), d(new Private(this))
411 {
412     d->init(&data);
413 }
414 
~KReportDesigner()415 KReportDesigner::~KReportDesigner()
416 {
417     delete d;
418 }
419 
420 ///The saving code
document() const421 QDomElement KReportDesigner::document() const
422 {
423     QDomDocument doc;
424     QString saveInterpreter;
425 
426     QDomElement content = doc.createElement(QLatin1String("report:content"));
427     content.setAttribute(QLatin1String("xmlns:report"), QLatin1String(ns));
428     content.setAttribute(QLatin1String("xmlns:fo"), QLatin1String("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"));
429     content.setAttribute(QLatin1String("xmlns:svg"), QLatin1String("urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"));
430 
431     doc.appendChild(content);
432 
433     //title
434     content.appendChild(propertyToElement(&doc, d->title));
435 
436 #ifdef KREPORT_SCRIPTING
437     if (d->originalInterpreter.isEmpty()) {
438         d->originalInterpreter = QLatin1String("javascript");
439     }
440     saveInterpreter = d->originalInterpreter;
441 
442     if (!d->script->value().toString().isEmpty()) {
443         if (d->script->value().toString() != d->originalScript || d->originalInterpreter == QLatin1String("qtscript") || d->originalInterpreter.isEmpty() ) {
444             //The script has changed so force interpreter to 'javascript'.  Also set if was using qtscript
445             saveInterpreter = QLatin1String("javascript");
446         }
447     }
448 
449     QDomElement scr = propertyToElement(&doc, d->script);
450     scr.setAttribute(QLatin1String("report:script-interpreter"), saveInterpreter);
451     content.appendChild(scr);
452 #endif
453 
454     QDomElement grd = doc.createElement(QLatin1String("report:grid"));
455     KReportUtils::addPropertyAsAttribute(&grd, d->showGrid);
456     KReportUtils::addPropertyAsAttribute(&grd, d->gridDivisions);
457     KReportUtils::addPropertyAsAttribute(&grd, d->gridSnap);
458     KReportUtils::addPropertyAsAttribute(&grd, d->unit);
459     content.appendChild(grd);
460 
461     // pageOptions
462     // -- size
463     QDomElement pagestyle = doc.createElement(QLatin1String("report:page-style"));
464 
465     if (d->pageSize->value().toString() == QLatin1String("Custom")) {
466         pagestyle.appendChild(doc.createTextNode(QLatin1String("custom")));
467 
468         KReportUtils::setAttribute(
469             &pagestyle, QLatin1String("report:custom-page-width"),
470             d->currentUnit.convertToPoint(d->customPageSize->value().toSizeF().width()));
471         KReportUtils::setAttribute(
472             &pagestyle, QLatin1String("report:custom-page-height"),
473             d->currentUnit.convertToPoint(d->customPageSize->value().toSizeF().height()));
474     } else if (d->pageSize->value().toString() == QLatin1String("Label")) {
475         pagestyle.appendChild(doc.createTextNode(QLatin1String("label")));
476         pagestyle.setAttribute(QLatin1String("report:page-label-type"), d->labelType->value().toString());
477     } else {
478         pagestyle.appendChild(doc.createTextNode(QLatin1String("predefined")));
479         KReportUtils::addPropertyAsAttribute(&pagestyle, d->pageSize);
480         //pagestyle.setAttribute("report:page-size", d->pageSize->value().toString());
481     }
482 
483     // -- orientation
484     KReportUtils::addPropertyAsAttribute(&pagestyle, d->orientation);
485 
486     // -- margins: save as points, and not localized
487     KReportUtils::setAttribute(
488         &pagestyle, QLatin1String("fo:margin-top"),
489         d->currentUnit.convertToPoint(d->topMargin->value().toDouble()));
490     KReportUtils::setAttribute(
491         &pagestyle, QLatin1String("fo:margin-bottom"),
492         d->currentUnit.convertToPoint(d->bottomMargin->value().toDouble()));
493     KReportUtils::setAttribute(
494         &pagestyle, QLatin1String("fo:margin-right"),
495         d->currentUnit.convertToPoint(d->rightMargin->value().toDouble()));
496     KReportUtils::setAttribute(
497         &pagestyle, QLatin1String("fo:margin-left"),
498         d->currentUnit.convertToPoint(d->leftMargin->value().toDouble()));
499 
500     content.appendChild(pagestyle);
501 
502     QDomElement body = doc.createElement(QLatin1String("report:body"));
503     QDomElement domsection;
504 
505     for (int i = static_cast<int>(KReportSectionData::Type::PageHeaderFirst);
506          i <= static_cast<int>(KReportSectionData::Type::PageFooterAny); ++i)
507     {
508         KReportDesignerSection *sec = section(static_cast<KReportSectionData::Type>(i));
509         if (sec) {
510             domsection = doc.createElement(QLatin1String("report:section"));
511             domsection.setAttribute(
512                 QLatin1String("report:section-type"),
513                 KReportSectionData::sectionTypeString(static_cast<KReportSectionData::Type>(i)));
514             sec->buildXML(&doc, &domsection);
515             body.appendChild(domsection);
516         }
517     }
518 
519     QDomElement detail = doc.createElement(QLatin1String("report:detail"));
520     d->detail->buildXML(&doc, &detail);
521     body.appendChild(detail);
522 
523     content.appendChild(body);
524     return content;
525 }
526 
slotSectionEditor()527 void KReportDesigner::slotSectionEditor()
528 {
529     KReportSectionEditor se(this);
530     (void)se.exec();
531 }
532 
setDataSource(KReportDataSource * source)533 void KReportDesigner::setDataSource(KReportDataSource* source)
534 {
535     if (d->dataSource == source) {
536         return;
537     }
538     delete d->dataSource;
539 
540     d->dataSource = source;
541     slotPageButton_Pressed();
542     setModified(true);
543     emit reportDataChanged();
544 }
545 
546 #ifdef KREPORT_SCRIPTING
setScriptSource(KReportScriptSource * source)547 void KReportDesigner::setScriptSource(KReportScriptSource* source)
548 {
549     d->scriptSource = source;
550 }
551 #endif
552 
section(KReportSectionData::Type type) const553 KReportDesignerSection * KReportDesigner::section(KReportSectionData::Type type) const
554 {
555     KReportDesignerSection *sec;
556     switch (type) {
557     case KReportSectionData::Type::PageHeaderAny:
558         sec = d->pageHeaderAny;
559         break;
560     case KReportSectionData::Type::PageHeaderEven:
561         sec = d->pageHeaderEven;
562         break;
563     case KReportSectionData::Type::PageHeaderOdd:
564         sec = d->pageHeaderOdd;
565         break;
566     case KReportSectionData::Type::PageHeaderFirst:
567         sec = d->pageHeaderFirst;
568         break;
569     case KReportSectionData::Type::PageHeaderLast:
570         sec = d->pageHeaderLast;
571         break;
572     case KReportSectionData::Type::PageFooterAny:
573         sec = d->pageFooterAny;
574         break;
575     case KReportSectionData::Type::PageFooterEven:
576         sec = d->pageFooterEven;
577         break;
578     case KReportSectionData::Type::PageFooterOdd:
579         sec = d->pageFooterOdd;
580         break;
581     case KReportSectionData::Type::PageFooterFirst:
582         sec = d->pageFooterFirst;
583         break;
584     case KReportSectionData::Type::PageFooterLast:
585         sec = d->pageFooterLast;
586         break;
587     case KReportSectionData::Type::ReportHeader:
588         sec = d->reportHeader;
589         break;
590     case KReportSectionData::Type::ReportFooter:
591         sec = d->reportFooter;
592         break;
593     default:
594         sec = nullptr;
595     }
596     return sec;
597 }
598 
createSection()599 KReportDesignerSection* KReportDesigner::createSection()
600 {
601     return new KReportDesignerSection(this, d->zoomHandler);
602 }
603 
removeSection(KReportSectionData::Type type)604 void KReportDesigner::removeSection(KReportSectionData::Type type)
605 {
606     KReportDesignerSection* sec = section(type);
607     if (sec) {
608         delete sec;
609 
610         switch (type) {
611         case KReportSectionData::Type::PageHeaderAny:
612             d->pageHeaderAny = nullptr;
613             break;
614         case KReportSectionData::Type::PageHeaderEven:
615             sec = d->pageHeaderEven = nullptr;
616             break;
617         case KReportSectionData::Type::PageHeaderOdd:
618             d->pageHeaderOdd = nullptr;
619             break;
620         case KReportSectionData::Type::PageHeaderFirst:
621             d->pageHeaderFirst = nullptr;
622             break;
623         case KReportSectionData::Type::PageHeaderLast:
624             d->pageHeaderLast = nullptr;
625             break;
626         case KReportSectionData::Type::PageFooterAny:
627             d->pageFooterAny = nullptr;
628             break;
629         case KReportSectionData::Type::PageFooterEven:
630             d->pageFooterEven = nullptr;
631             break;
632         case KReportSectionData::Type::PageFooterOdd:
633             d->pageFooterOdd = nullptr;
634             break;
635         case KReportSectionData::Type::PageFooterFirst:
636             d->pageFooterFirst = nullptr;
637             break;
638         case KReportSectionData::Type::PageFooterLast:
639             d->pageFooterLast = nullptr;
640             break;
641         case KReportSectionData::Type::ReportHeader:
642             d->reportHeader = nullptr;
643             break;
644         case KReportSectionData::Type::ReportFooter:
645             d->reportFooter = nullptr;
646             break;
647         default:
648             sec = nullptr;
649         }
650 
651         setModified(true);
652         adjustSize();
653     }
654 }
655 
insertSection(KReportSectionData::Type type)656 void KReportDesigner::insertSection(KReportSectionData::Type type)
657 {
658     KReportDesignerSection* sec = section(type);
659     if (!sec) {
660         int idx = 0;
661         for (int i = static_cast<int>(KReportSectionData::Type::PageHeaderFirst);
662              i <= static_cast<int>(type); ++i)
663         {
664             if (section(static_cast<KReportSectionData::Type>(i)))
665                 idx++;
666         }
667         if (type > KReportSectionData::Type::ReportHeader)
668             idx++;
669         //kreportDebug() << idx;
670         KReportDesignerSection *rs = createSection();
671         d->vboxlayout->insertWidget(idx, rs);
672 
673         switch (type) {
674         case KReportSectionData::Type::PageHeaderAny:
675             rs->setTitle(tr("Page Header (Any)"));
676             d->pageHeaderAny = rs;
677             break;
678         case KReportSectionData::Type::PageHeaderEven:
679             rs->setTitle(tr("Page Header (Even)"));
680             d->pageHeaderEven = rs;
681             break;
682         case KReportSectionData::Type::PageHeaderOdd:
683             rs->setTitle(tr("Page Header (Odd)"));
684             d->pageHeaderOdd = rs;
685             break;
686         case KReportSectionData::Type::PageHeaderFirst:
687             rs->setTitle(tr("Page Header (First)"));
688             d->pageHeaderFirst = rs;
689             break;
690         case KReportSectionData::Type::PageHeaderLast:
691             rs->setTitle(tr("Page Header (Last)"));
692             d->pageHeaderLast = rs;
693             break;
694         case KReportSectionData::Type::PageFooterAny:
695             rs->setTitle(tr("Page Footer (Any)"));
696             d->pageFooterAny = rs;
697             break;
698         case KReportSectionData::Type::PageFooterEven:
699             rs->setTitle(tr("Page Footer (Even)"));
700             d->pageFooterEven = rs;
701             break;
702         case KReportSectionData::Type::PageFooterOdd:
703             rs->setTitle(tr("Page Footer (Odd)"));
704             d->pageFooterOdd = rs;
705             break;
706         case KReportSectionData::Type::PageFooterFirst:
707             rs->setTitle(tr("Page Footer (First)"));
708             d->pageFooterFirst = rs;
709             break;
710         case KReportSectionData::Type::PageFooterLast:
711             rs->setTitle(tr("Page Footer (Last)"));
712             d->pageFooterLast = rs;
713             break;
714         case KReportSectionData::Type::ReportHeader:
715             rs->setTitle(tr("Report Header"));
716             d->reportHeader = rs;
717             break;
718         case KReportSectionData::Type::ReportFooter:
719             rs->setTitle(tr("Report Footer"));
720             d->reportFooter = rs;
721             break;
722             //These sections cannot be inserted this way
723         case KReportSectionData::Type::None:
724         case KReportSectionData::Type::GroupHeader:
725         case KReportSectionData::Type::GroupFooter:
726         case KReportSectionData::Type::Detail:
727             break;
728         }
729 
730         rs->show();
731         setModified(true);
732         adjustSize();
733         emit pagePropertyChanged(d->set);
734     }
735 }
736 
setReportTitle(const QString & str)737 void KReportDesigner::setReportTitle(const QString & str)
738 {
739     if (reportTitle() != str) {
740         d->title->setValue(str);
741         setModified(true);
742     }
743 }
744 
propertySet() const745 KPropertySet* KReportDesigner::propertySet() const
746 {
747     return &d->set;
748 }
749 
selectedItemPropertySet() const750 KPropertySet* KReportDesigner::selectedItemPropertySet() const
751 {
752     return d->itemSet;
753 }
754 
reportDataSource() const755 KReportDataSource *KReportDesigner::reportDataSource() const
756 {
757     return d->dataSource;
758 }
759 
detailSection() const760 KReportDesignerSectionDetail * KReportDesigner::detailSection() const
761 {
762     return d->detail;
763 }
764 
reportTitle() const765 QString KReportDesigner::reportTitle() const
766 {
767     return d->title->value().toString();
768 }
769 
isModified() const770 bool KReportDesigner::isModified() const
771 {
772     return d->modified;
773 }
774 
setModified(bool modified)775 void KReportDesigner::setModified(bool modified)
776 {
777     d->modified = modified;
778 
779     if (d->modified) {
780         emit dirty();
781     }
782 }
783 
fieldNames() const784 QStringList KReportDesigner::fieldNames() const
785 {
786     QStringList qs;
787     qs << QString();
788     if (d->dataSource)
789         qs << d->dataSource->fieldNames();
790 
791     return qs;
792 }
793 
fieldKeys() const794 QStringList KReportDesigner::fieldKeys() const
795 {
796     QStringList qs;
797     qs << QString();
798     if (d->dataSource)
799         qs << d->dataSource->fieldKeys();
800 
801     return qs;
802 }
803 
createProperties()804 void KReportDesigner::createProperties()
805 {
806     KReportDesigner::addMetaProperties(&d->set,
807         tr("Report", "Main report element"), QLatin1String("kreport-report-element"));
808 
809     connect(&d->set, SIGNAL(propertyChanged(KPropertySet&,KProperty&)),
810             this, SLOT(slotPropertyChanged(KPropertySet&,KProperty&)));
811 
812     d->title = new KProperty("title", QLatin1String("Report"), tr("Title"), tr("Report Title"));
813 
814     KPropertyListData *listData = new KPropertyListData(KReportPageSize::pageFormatKeys(),
815                                                         KReportPageSize::pageFormatNames());
816     QVariant defaultKey = KReportPageSize::pageSizeKey(KReportPageSize::defaultSize());
817     d->pageSize = new KProperty("page-size", listData, defaultKey, tr("Page Size"));
818 
819     d->customPageSize = new KProperty("custom-page-size", DEFAULT_CUSTOM_PAGE_SIZE,
820         tr("Custom Page Size"), tr("Custom Page Size"), KProperty::SizeF);
821     d->customPageSize->setOption("suffix", d->currentUnit.symbol());
822 
823     listData = new KPropertyListData({ QLatin1String("portrait"), QLatin1String("landscape") },
824                                      QVariantList{ tr("Portrait"), tr("Landscape") });
825     d->orientation = new KProperty("print-orientation", listData, QLatin1String("portrait"),
826                                    tr("Page Orientation"));
827 
828     QList<KReportUnit::Type> types(KReportUnit::allTypes());
829     types.removeOne(KReportUnit::Type::Pixel);
830     listData = new KPropertyListData(KReportUnit::symbols(types), KReportUnit::descriptions(types));
831     d->unit = new KProperty("page-unit", listData, DEFAULT_UNIT_STRING, tr("Page Unit"));
832     d->showGrid = new KProperty("grid-visible", true, tr("Show Grid"));
833     d->gridSnap = new KProperty("grid-snap", true, tr("Snap to Grid"));
834     d->gridDivisions = new KProperty("grid-divisions", 4, tr("Grid Divisions"));
835     d->gridDivisions->setOption("min", 1);
836     d->gridDivisions->setOption("max", 10);
837 
838 
839     d->leftMargin = new KProperty("margin-left", pageUnit().convertFromPoint(KReportUnit::parseValue(DEFAULT_PAGE_MARGIN_STRING)),
840         tr("Left Margin"), tr("Left Margin"), KProperty::Double);
841     d->rightMargin = new KProperty("margin-right", pageUnit().convertFromPoint(KReportUnit::parseValue(DEFAULT_PAGE_MARGIN_STRING)),
842         tr("Right Margin"), tr("Right Margin"), KProperty::Double);
843     d->topMargin = new KProperty("margin-top", pageUnit().convertFromPoint(KReportUnit::parseValue(DEFAULT_PAGE_MARGIN_STRING)),
844         tr("Top Margin"), tr("Top Margin"), KProperty::Double);
845     d->bottomMargin = new KProperty("margin-bottom", pageUnit().convertFromPoint(KReportUnit::parseValue(DEFAULT_PAGE_MARGIN_STRING)),
846         tr("Bottom Margin"), tr("Bottom Margin"), KProperty::Double);
847     d->leftMargin->setOption("suffix", d->currentUnit.symbol());
848     d->rightMargin->setOption("suffix", d->currentUnit.symbol());
849     d->topMargin->setOption("suffix", d->currentUnit.symbol());
850     d->bottomMargin->setOption("suffix", d->currentUnit.symbol());
851 
852     d->set.addProperty(d->title);
853     d->set.addProperty(d->pageSize);
854     d->set.addProperty(d->customPageSize);
855     d->set.addProperty(d->orientation);
856     d->set.addProperty(d->unit);
857     d->set.addProperty(d->gridSnap);
858     d->set.addProperty(d->showGrid);
859     d->set.addProperty(d->gridDivisions);
860     d->set.addProperty(d->leftMargin);
861     d->set.addProperty(d->rightMargin);
862     d->set.addProperty(d->topMargin);
863     d->set.addProperty(d->bottomMargin);
864 
865     recalculateMaxMargins();
866 
867 #ifdef KREPORT_SCRIPTING
868     d->script = new KProperty("script", new KPropertyListData, QVariant(), tr("Object Script"));
869     d->set.addProperty(d->script);
870 #endif
871 }
872 
873 /**
874 @brief Handle property changes
875 */
slotPropertyChanged(KPropertySet & s,KProperty & p)876 void KReportDesigner::slotPropertyChanged(KPropertySet &s, KProperty &p)
877 {
878     const QSignalBlocker blocker(s);
879     setModified(true);
880     QByteArray propertyName = p.name();
881 
882     if (propertyName == "page-unit") {
883         const KReportUnit oldUnit = d->currentUnit;
884         d->updateCurrentUnit();
885         d->hruler->setUnit(pageUnit());
886 
887         // convert values
888         d->leftMargin->setValue(KReportUnit::convertFromUnitToUnit(
889                                     d->leftMargin->value().toDouble(), oldUnit, d->currentUnit),
890                                 KProperty::ValueOption::IgnoreOld);
891 
892         d->rightMargin->setValue(KReportUnit::convertFromUnitToUnit(
893                                      d->rightMargin->value().toDouble(), oldUnit, d->currentUnit),
894                                  KProperty::ValueOption::IgnoreOld);
895 
896         d->topMargin->setValue(KReportUnit::convertFromUnitToUnit(d->topMargin->value().toDouble(),
897                                                                   oldUnit, d->currentUnit),
898                                KProperty::ValueOption::IgnoreOld);
899 
900         d->bottomMargin->setValue(KReportUnit::convertFromUnitToUnit(
901                                       d->bottomMargin->value().toDouble(), oldUnit, d->currentUnit),
902                                   KProperty::ValueOption::IgnoreOld);
903 
904         d->customPageSize->setValue(
905             KReportUnit::convertFromUnitToUnit(d->customPageSize->value().toSizeF(), oldUnit,
906                                                d->currentUnit),
907             KProperty::ValueOption::IgnoreOld);
908 
909         d->leftMargin->setOption("suffix", d->currentUnit.symbol());
910         d->rightMargin->setOption("suffix", d->currentUnit.symbol());
911         d->topMargin->setOption("suffix", d->currentUnit.symbol());
912         d->bottomMargin->setOption("suffix", d->currentUnit.symbol());
913         d->customPageSize->setOption("suffix", d->currentUnit.symbol());
914     } else if (propertyName.startsWith("margin-") || propertyName == "page-size" || propertyName == "custom-page-size") {
915         recalculateMaxMargins();
916     }
917     emit pagePropertyChanged(s);
918 
919 }
920 
slotPageButton_Pressed()921 void KReportDesigner::slotPageButton_Pressed()
922 {
923 #ifdef KREPORT_SCRIPTING
924     d->updateScripts();
925     changeSet(&d->set);
926 #endif
927 }
928 
sizeHint() const929 QSize KReportDesigner::sizeHint() const
930 {
931     int w = 0;
932     int h = 0;
933 
934     if (d->pageFooterAny)
935         h += d->pageFooterAny->sizeHint().height();
936     if (d->pageFooterEven)
937         h += d->pageFooterEven->sizeHint().height();
938     if (d->pageFooterFirst)
939         h += d->pageFooterFirst->sizeHint().height();
940     if (d->pageFooterLast)
941         h += d->pageFooterLast->sizeHint().height();
942     if (d->pageFooterOdd)
943         h += d->pageFooterOdd->sizeHint().height();
944     if (d->pageHeaderAny)
945         h += d->pageHeaderAny->sizeHint().height();
946     if (d->pageHeaderEven)
947         h += d->pageHeaderEven->sizeHint().height();
948     if (d->pageHeaderFirst)
949         h += d->pageHeaderFirst->sizeHint().height();
950     if (d->pageHeaderLast)
951         h += d->pageHeaderLast->sizeHint().height();
952     if (d->pageHeaderOdd)
953         h += d->pageHeaderOdd->sizeHint().height();
954     if (d->reportHeader)
955         h += d->reportHeader->sizeHint().height();
956     if (d->reportFooter) {
957         h += d->reportFooter->sizeHint().height();
958 
959     }
960     if (d->detail) {
961         h += d->detail->sizeHint().height();
962         w += d->detail->sizeHint().width();
963     }
964 
965     h += d->hruler->height();
966 
967     return QSize(w, h);
968 }
969 
pageWidthPx() const970 int KReportDesigner::pageWidthPx() const
971 {
972     QSize pageSizePx;
973     int pageWidth;
974 
975     if (d->set.property("page-size").value().toString() == QLatin1String("Custom")) {
976         KReportUnit unit = pageUnit();
977 
978         QSizeF customSize = d->currentUnit.convertToPoint(d->set.property("custom-page-size").value().toSizeF());
979         QPageLayout layout(QPageSize(customSize, QPageSize::Point, QString(), QPageSize::ExactMatch), d->set.property("print-orientation").value().toString()
980                     == QLatin1String("portrait") ? QPageLayout::Portrait : QPageLayout::Landscape, QMarginsF(0,0,0,0));
981 
982         pageSizePx = layout.fullRectPixels(KReportPrivate::dpiX()).size();
983     } else {
984         QPageLayout layout = QPageLayout(
985             QPageSize(KReportPageSize::pageSize(d->set.property("page-size").value().toString())),
986             d->set.property("print-orientation").value().toString()
987                     == QLatin1String("portrait") ? QPageLayout::Portrait : QPageLayout::Landscape, QMarginsF(0,0,0,0));
988         pageSizePx = layout.fullRectPixels(KReportPrivate::dpiX()).size();
989     }
990 
991     pageWidth = pageSizePx.width();
992 
993     pageWidth = pageWidth - KReportUnit::convertFromUnitToUnit(d->set.property("margin-left").value().toDouble(), pageUnit(), KReportUnit(KReportUnit::Type::Inch)) * KReportPrivate::dpiX();
994     pageWidth = pageWidth - KReportUnit::convertFromUnitToUnit(d->set.property("margin-right").value().toDouble(), pageUnit(), KReportUnit(KReportUnit::Type::Inch)) * KReportPrivate::dpiX();
995 
996     return pageWidth;
997 }
998 
pageSizePt() const999 QSize KReportDesigner::pageSizePt() const
1000 {
1001     QSize pageSizePt;
1002 
1003     if (d->set.property("page-size").value().toString() == QLatin1String("Custom")) {
1004         KReportUnit unit = pageUnit();
1005 
1006         QSizeF customSize = d->currentUnit.convertToPoint(d->set.property("custom-page-size").value().toSizeF());
1007         QPageLayout layout(QPageSize(customSize, QPageSize::Point, QString(), QPageSize::ExactMatch), d->set.property("print-orientation").value().toString()
1008                     == QLatin1String("portrait") ? QPageLayout::Portrait : QPageLayout::Landscape, QMarginsF(0,0,0,0));
1009 
1010         pageSizePt = layout.fullRectPoints().size();
1011     } else {
1012         QPageLayout layout = QPageLayout(
1013             QPageSize(KReportPageSize::pageSize(d->set.property("page-size").value().toString())),
1014             d->set.property("print-orientation").value().toString()
1015                     == QLatin1String("portrait") ? QPageLayout::Portrait : QPageLayout::Landscape, QMarginsF(0,0,0,0));
1016         pageSizePt = layout.fullRectPoints().size();
1017     }
1018 
1019     return pageSizePt;
1020 }
1021 
resizeEvent(QResizeEvent * event)1022 void KReportDesigner::resizeEvent(QResizeEvent * event)
1023 {
1024     Q_UNUSED(event);
1025     d->hruler->setRulerLength(pageWidthPx());
1026 }
1027 
setDetail(KReportDesignerSectionDetail * rsd)1028 void KReportDesigner::setDetail(KReportDesignerSectionDetail *rsd)
1029 {
1030     if (!d->detail) {
1031         int idx = 0;
1032         if (d->pageHeaderFirst) idx++;
1033         if (d->pageHeaderOdd) idx++;
1034         if (d->pageHeaderEven) idx++;
1035         if (d->pageHeaderLast) idx++;
1036         if (d->pageHeaderAny) idx++;
1037         if (d->reportHeader) idx++;
1038         d->detail = rsd;
1039         d->vboxlayout->insertWidget(idx, d->detail);
1040     }
1041 }
1042 
pageUnit() const1043 KReportUnit KReportDesigner::pageUnit() const
1044 {
1045     return d->currentUnit;
1046 }
1047 
setGridOptions(bool vis,int div)1048 void KReportDesigner::setGridOptions(bool vis, int div)
1049 {
1050     d->showGrid->setValue(QVariant(vis));
1051     d->gridDivisions->setValue(div);
1052 }
1053 
1054 //
1055 // methods for the sectionMouse*Event()
1056 //
sectionContextMenuEvent(KReportDesignerSectionScene * s,QGraphicsSceneContextMenuEvent * e)1057 void KReportDesigner::sectionContextMenuEvent(KReportDesignerSectionScene * s, QGraphicsSceneContextMenuEvent * e)
1058 {
1059     Q_UNUSED(s);
1060 
1061     QMenu pop;
1062 
1063     bool itemsSelected = selectionCount() > 0;
1064     if (itemsSelected) {
1065         //! @todo KF5 use KStandardAction
1066         QAction *a = new QAction(QIcon::fromTheme(QLatin1String("edit-cut")), tr("Cut"), this);
1067         connect(a, SIGNAL(triggered()), this, SLOT(slotEditCut()));
1068         pop.addAction(a);
1069         //! @todo KF5 use KStandardAction
1070         a = new QAction(QIcon::fromTheme(QLatin1String("edit-copy")), tr("Copy"), this);
1071         connect(a, SIGNAL(triggered()), this, SLOT(slotEditCopy()));
1072         pop.addAction(a);
1073     }
1074     if (!d->sectionData.copy_list.isEmpty()) {
1075         QAction *a = new QAction(QIcon::fromTheme(QLatin1String("edit-paste")), tr("Paste"), this);
1076         connect(a, SIGNAL(triggered()), this, SLOT(slotEditPaste()));
1077         pop.addAction(a);
1078     }
1079 
1080     if (itemsSelected) {
1081         pop.addSeparator();
1082         //! @todo KF5 use KStandard*
1083         //const KGuiItem del = KStandardGuiItem::del();
1084         //a->setToolTip(del.toolTip());
1085         //a->setShortcut(QKeySequence(QKeySequence::Delete));
1086         QAction *a = new QAction(QIcon::fromTheme(QLatin1String("edit-delete")), tr("Delete"), this);
1087         connect(a, SIGNAL(triggered()), SLOT(slotEditDelete()));
1088         pop.addAction(a);
1089     }
1090     if (!pop.actions().isEmpty()) {
1091         pop.exec(e->screenPos());
1092     }
1093 }
1094 
sectionMousePressEvent(KReportDesignerSectionView * v,QMouseEvent * e)1095 void KReportDesigner::sectionMousePressEvent(KReportDesignerSectionView * v, QMouseEvent * e)
1096 {
1097     Q_UNUSED(v);
1098     d->pressX = e->pos().x();
1099     d->pressY = e->pos().y();
1100 }
1101 
sectionMouseReleaseEvent(KReportDesignerSectionView * v,QMouseEvent * e)1102 void KReportDesigner::sectionMouseReleaseEvent(KReportDesignerSectionView * v, QMouseEvent * e)
1103 {
1104     e->accept();
1105 
1106     d->releaseX = e->pos().x();
1107     d->releaseY = e->pos().y();
1108 
1109     if (e->button() == Qt::LeftButton) {
1110         QPointF pos(d->pressX, d->pressY);
1111         QPointF end(d->releaseX, d->releaseY);
1112         if (d->releaseY >= v->scene()->height()) {
1113             d->releaseY = v->scene()->height();
1114             end.setY(v->scene()->height());
1115         }
1116 
1117         if (d->releaseX >= v->scene()->width()) {
1118             d->releaseX = v->scene()->width();
1119             end.setX(v->scene()->width());
1120         }
1121 
1122         if (d->sectionData.mouseAction == ReportWriterSectionData::MouseAction::Insert) {
1123             QGraphicsItem * item = nullptr;
1124             QString classString;
1125             QString iconName;
1126             if (d->sectionData.itemToInsert == QLatin1String("org.kde.kreport.line")) {
1127                 item = new KReportDesignerItemLine(v->designer(), v->scene(), pos, end);
1128                 classString = tr("Line", "Report line element");
1129                 iconName = QLatin1String("kreport-line-element");
1130             }
1131             else {
1132                 KReportPluginManager* pluginManager = KReportPluginManager::self();
1133                 KReportPluginInterface *plug = pluginManager->plugin(d->sectionData.itemToInsert);
1134                 if (plug) {
1135                     QObject *obj = plug->createDesignerInstance(v->designer(), v->scene(), pos);
1136                     if (obj) {
1137                         item = dynamic_cast<QGraphicsItem*>(obj);
1138                         classString = plug->metaData()->name();
1139                         iconName = plug->metaData()->iconName();
1140                     }
1141                 }
1142                 else {
1143                     kreportWarning() << "attempted to insert an unknown item";
1144                 }
1145             }
1146             if (item) {
1147                 item->setVisible(true);
1148                 item->setSelected(true);
1149                 KReportItemBase* baseReportItem = dynamic_cast<KReportItemBase*>(item);
1150                 if (baseReportItem) {
1151                     KPropertySet *set = baseReportItem->propertySet();
1152                     KReportDesigner::addMetaProperties(set, classString, iconName);
1153                     set->clearModifiedFlags();
1154                     changeSet(set);
1155                     if (v && v->designer()) {
1156                         v->designer()->setModified(true);
1157                     }
1158                     emit itemInserted(d->sectionData.itemToInsert);
1159                 }
1160             }
1161 
1162             d->sectionData.mouseAction = ReportWriterSectionData::MouseAction::None;
1163             d->sectionData.itemToInsert.clear();
1164             unsetSectionCursor();
1165         }
1166     }
1167 }
1168 
selectionCount() const1169 unsigned int KReportDesigner::selectionCount() const
1170 {
1171     if (activeScene())
1172         return activeScene()->selectedItems().count();
1173     else
1174         return 0;
1175 }
1176 
changeSet(KPropertySet * set)1177 void KReportDesigner::changeSet(KPropertySet *set)
1178 {
1179     //Set the checked state of the report properties button
1180     if (set == &d->set)
1181         d->pageButton->setCheckState(Qt::Checked);
1182     else
1183         d->pageButton->setCheckState(Qt::Unchecked);
1184 
1185     if (d->itemSet != set) {
1186         d->itemSet = set;
1187         emit propertySetChanged();
1188     }
1189 }
1190 
1191 //
1192 // Actions
1193 //
1194 
slotItem(const QString & entity)1195 void KReportDesigner::slotItem(const QString &entity)
1196 {
1197     //kreportDebug() << entity;
1198     d->sectionData.mouseAction = ReportWriterSectionData::MouseAction::Insert;
1199     d->sectionData.itemToInsert = entity;
1200     setSectionCursor(QCursor(Qt::CrossCursor));
1201 }
1202 
slotEditDelete()1203 void KReportDesigner::slotEditDelete()
1204 {
1205     QGraphicsItem * item = nullptr;
1206     bool modified = false;
1207     while (selectionCount() > 0) {
1208         item = activeScene()->selectedItems().value(0);
1209         if (item) {
1210             QGraphicsScene * scene = item->scene();
1211             delete item;
1212             scene->update();
1213             d->sectionData.mouseAction = ReportWriterSectionData::MouseAction::None;
1214             modified = true;
1215         }
1216     }
1217     activeScene()->selectedItems().clear();
1218 
1219     /*! @todo temporary: clears cut and copy lists to make sure we do not crash
1220      if weve deleted something in the list
1221      should really check if an item is in the list first
1222      and remove it. */
1223     d->sectionData.cut_list.clear();
1224     d->sectionData.copy_list.clear();
1225     if (modified) {
1226         setModified(true);
1227     }
1228 }
1229 
slotEditCut()1230 void KReportDesigner::slotEditCut()
1231 {
1232     if (selectionCount() > 0) {
1233         //First delete any items that are curerntly in the list
1234         //so as not to leak memory
1235         qDeleteAll(d->sectionData.cut_list);
1236         d->sectionData.cut_list.clear();
1237 
1238         QGraphicsItem * item = activeScene()->selectedItems().first();
1239         bool modified = false;
1240         if (item) {
1241             d->sectionData.copy_list.clear();
1242             foreach(QGraphicsItem *item, activeScene()->selectedItems()) {
1243                 d->sectionData.cut_list.append(dynamic_cast<KReportDesignerItemBase*>(item));
1244                 d->sectionData.copy_list.append(dynamic_cast<KReportDesignerItemBase*>(item));
1245             }
1246             foreach(QGraphicsItem *item, activeScene()->selectedItems()) {
1247                 activeScene()->removeItem(item);
1248                 activeScene()->update();
1249                 modified = true;
1250             }
1251             d->sectionData.selected_x_offset = 10;
1252             d->sectionData.selected_y_offset = 10;
1253         }
1254         if (modified) {
1255             setModified(true);
1256         }
1257     }
1258 }
1259 
slotEditCopy()1260 void KReportDesigner::slotEditCopy()
1261 {
1262     if (selectionCount() < 1)
1263         return;
1264 
1265     QGraphicsItem * item = activeScene()->selectedItems().first();
1266     if (item) {
1267         d->sectionData.copy_list.clear();
1268         foreach(QGraphicsItem *item, activeScene()->selectedItems()) {
1269             d->sectionData.copy_list.append(dynamic_cast<KReportDesignerItemBase*>(item));
1270         }
1271         d->sectionData.selected_x_offset = 10;
1272         d->sectionData.selected_y_offset = 10;
1273     }
1274 }
1275 
slotEditPaste()1276 void KReportDesigner::slotEditPaste()
1277 {
1278     // call the editPaste function passing it a reportsection
1279     slotEditPaste(activeScene());
1280 }
1281 
slotEditPaste(QGraphicsScene * canvas)1282 void KReportDesigner::slotEditPaste(QGraphicsScene * canvas)
1283 {
1284 
1285     // paste a new item of the copy we have in the specified location
1286     if (!d->sectionData.copy_list.isEmpty()) {
1287         QList<QGraphicsItem*> activeItems = canvas->selectedItems();
1288         QGraphicsItem *activeItem = nullptr;
1289         if (activeItems.count() == 1) {
1290             activeItem = activeItems.first();
1291         }
1292         canvas->clearSelection();
1293         d->sectionData.mouseAction = ReportWriterSectionData::MouseAction::None;
1294 
1295         //! @todo this code sucks :)
1296         //! The setPos calls only work AFTER the name has been set ?!?!?
1297 
1298         foreach(KReportDesignerItemBase *item, d->sectionData.copy_list) {
1299             KReportItemBase *obj = dynamic_cast<KReportItemBase*>(item);
1300             const QString type = obj ? obj->typeName() : QLatin1String("object");
1301             //kreportDebug() << type;
1302             KReportDesignerItemBase *ent = item->clone();
1303             KReportItemBase *new_obj = dynamic_cast<KReportItemBase*>(ent);
1304             if (new_obj) {
1305                 new_obj->setEntityName(suggestEntityName(type));
1306                 if (activeItem) {
1307                     new_obj->setPosition(KReportItemBase::positionFromScene(QPointF(activeItem->x() + 10, activeItem->y() + 10)));
1308                 } else {
1309                     new_obj->setPosition(KReportItemBase::positionFromScene(QPointF(0, 0)));
1310                 }
1311                 new_obj->propertySet()->clearModifiedFlags();
1312                 changeSet(new_obj->propertySet());
1313             }
1314             QGraphicsItem *pasted_ent = dynamic_cast<QGraphicsItem*>(ent);
1315             if (pasted_ent) {
1316                 pasted_ent->setSelected(true);
1317                 canvas->addItem(pasted_ent);
1318                 pasted_ent->show();
1319                 d->sectionData.mouseAction = ReportWriterSectionData::MouseAction::Grab;
1320                 setModified(true);
1321             }
1322         }
1323     }
1324 }
slotRaiseSelected()1325 void KReportDesigner::slotRaiseSelected()
1326 {
1327     dynamic_cast<KReportDesignerSectionScene*>(activeScene())->raiseSelected();
1328 }
1329 
slotLowerSelected()1330 void KReportDesigner::slotLowerSelected()
1331 {
1332     dynamic_cast<KReportDesignerSectionScene*>(activeScene())->lowerSelected();
1333 }
1334 
activeScene() const1335 QGraphicsScene* KReportDesigner::activeScene() const
1336 {
1337     return d->activeScene;
1338 }
1339 
setActiveScene(QGraphicsScene * a)1340 void KReportDesigner::setActiveScene(QGraphicsScene* a)
1341 {
1342     if (d->activeScene && d->activeScene != a)
1343         d->activeScene->clearSelection();
1344     d->activeScene = a;
1345 
1346     //Trigger an update so that the last scene redraws its title;
1347     update();
1348 }
1349 
suggestEntityName(const QString & name) const1350 QString KReportDesigner::suggestEntityName(const QString &name) const
1351 {
1352     KReportDesignerSection *sec;
1353     int itemCount = 0;
1354     // Count items in the main sections
1355     for (int i = static_cast<int>(KReportSectionData::Type::PageHeaderFirst);
1356          i <= static_cast<int>(KReportSectionData::Type::PageFooterAny); i++)
1357     {
1358         sec = section(static_cast<KReportSectionData::Type>(i));
1359         if (sec) {
1360             const QGraphicsItemList l = sec->items();
1361             itemCount += l.count();
1362         }
1363     }
1364 
1365     if (d->detail) {
1366         //Count items in the group headers/footers
1367         for (int i = 0; i < d->detail->groupSectionCount(); i++) {
1368             sec = d->detail->groupSection(i)->groupHeader();
1369             if (sec) {
1370                 const QGraphicsItemList l = sec->items();
1371                 itemCount += l.count();
1372             }
1373             sec = d->detail->groupSection(i)->groupFooter();
1374             if (sec) {
1375                 const QGraphicsItemList l = sec->items();
1376                 itemCount += l.count();
1377             }
1378         }
1379 
1380         sec = d->detail->detailSection();
1381         if (sec) {
1382             const QGraphicsItemList l = sec->items();
1383             itemCount += l.count();
1384         }
1385     }
1386 
1387     while (!isEntityNameUnique(name + QString::number(itemCount))) {
1388         itemCount++;
1389     }
1390     return name + QString::number(itemCount);
1391 }
1392 
isEntityNameUnique(const QString & name,KReportItemBase * ignore) const1393 bool KReportDesigner::isEntityNameUnique(const QString &name, KReportItemBase* ignore) const
1394 {
1395     KReportDesignerSection *sec;
1396     bool unique = true;
1397 
1398     // Check items in the main sections
1399     for (int i = static_cast<int>(KReportSectionData::Type::PageHeaderFirst);
1400          i <= static_cast<int>(KReportSectionData::Type::PageFooterAny); i++)
1401     {
1402         sec = section(static_cast<KReportSectionData::Type>(i));
1403         if (sec) {
1404             const QGraphicsItemList l = sec->items();
1405             for (QGraphicsItemList::const_iterator it = l.constBegin(); it != l.constEnd(); ++it) {
1406                 KReportItemBase* itm = dynamic_cast<KReportItemBase*>(*it);
1407                 if (itm && itm->entityName() == name  && itm != ignore) {
1408                     unique = false;
1409                     break;
1410                 }
1411             }
1412             if (!unique) break;
1413         }
1414     }
1415 
1416     //Count items in the group headers/footers
1417     if (unique && d->detail) {
1418         for (int i = 0; i < d->detail->groupSectionCount(); ++i) {
1419             sec = d->detail->groupSection(i)->groupHeader();
1420             if (sec) {
1421                 const QGraphicsItemList l = sec->items();
1422                 for (QGraphicsItemList::const_iterator it = l.constBegin(); it != l.constEnd(); ++it) {
1423                     KReportItemBase* itm = dynamic_cast<KReportItemBase*>(*it);
1424                     if (itm && itm->entityName() == name  && itm != ignore) {
1425                         unique = false;
1426                         break;
1427                     }
1428                 }
1429 
1430             }
1431             sec = d->detail->groupSection(i)->groupFooter();
1432             if (unique && sec) {
1433                 const QGraphicsItemList l = sec->items();
1434                 for (QGraphicsItemList::const_iterator it = l.constBegin(); it != l.constEnd(); ++it) {
1435                     KReportItemBase* itm = dynamic_cast<KReportItemBase*>(*it);
1436                     if (itm && itm->entityName() == name  && itm != ignore) {
1437                         unique = false;
1438                         break;
1439                     }
1440                 }
1441             }
1442         }
1443     }
1444     if (unique && d->detail) {
1445         sec = d->detail->detailSection();
1446         if (sec) {
1447             const QGraphicsItemList l = sec->items();
1448             for (QGraphicsItemList::const_iterator it = l.constBegin(); it != l.constEnd(); ++it) {
1449                 KReportItemBase* itm = dynamic_cast<KReportItemBase*>(*it);
1450                 if (itm && itm->entityName() == name  && itm != ignore) {
1451                     unique = false;
1452                     break;
1453                 }
1454             }
1455         }
1456     }
1457 
1458     return unique;
1459 }
1460 
actionPriortyLessThan(QAction * act1,QAction * act2)1461 static bool actionPriortyLessThan(QAction* act1, QAction* act2)
1462 {
1463     if (act1->data().toInt() > 0 && act2->data().toInt() > 0) {
1464         return act1->data().toInt() < act2->data().toInt();
1465     }
1466     return false;
1467 }
1468 
itemActions(QActionGroup * group)1469 QList<QAction*> KReportDesigner::itemActions(QActionGroup* group)
1470 {
1471     KReportPluginManager* manager = KReportPluginManager::self();
1472     QList<QAction*> actList = manager->createActions(group);
1473 
1474     //! @todo make line a real plugin so this isn't needed:
1475     QAction *act = new QAction(QIcon::fromTheme(QLatin1String("kreport-line-element")), tr("Line"), group);
1476     act->setObjectName(QLatin1String("org.kde.kreport.line"));
1477     act->setData(9);
1478     act->setCheckable(true);
1479     actList << act;
1480 
1481     qSort(actList.begin(), actList.end(), actionPriortyLessThan);
1482     int i = 0;
1483 
1484     /*! @todo maybe this is a bit hackish
1485      It finds the first plugin based on the priority in userdata
1486      The lowest oriority a plugin can have is 10
1487      And inserts a separator before it. */
1488     bool sepInserted = false;
1489     foreach(QAction *a, actList) {
1490         ++i;
1491         if (!sepInserted && a->data().toInt() >= 10) {
1492             QAction *sep = new QAction(QLatin1String("separator"), group);
1493             sep->setSeparator(true);
1494             actList.insert(i-1, sep);
1495             sepInserted = true;
1496         }
1497         if (group) {
1498             group->addAction(a);
1499         }
1500     }
1501 
1502     return actList;
1503 }
1504 
designerActions()1505 QList< QAction* > KReportDesigner::designerActions()
1506 {
1507     QList<QAction*> al;
1508     QAction *sep = new QAction(QString(), this);
1509     sep->setSeparator(true);
1510 
1511     al << d->editCutAction << d->editCopyAction << d->editPasteAction << d->editDeleteAction << sep << d->sectionEdit << sep << d->itemLowerAction << d->itemRaiseAction;
1512 
1513     return al;
1514 }
1515 
createActions()1516 void KReportDesigner::createActions()
1517 {
1518     d->editCutAction = new QAction(QIcon::fromTheme(QLatin1String("edit-cut")), tr("Cu&t"), this);
1519     d->editCutAction->setObjectName(QLatin1String("edit_cut"));
1520     d->editCutAction->setToolTip(tr("Cut selection to clipboard"));
1521     d->editCutAction->setShortcuts(KStandardShortcut::cut());
1522     d->editCutAction->setProperty("iconOnly", true);
1523 
1524     d->editCopyAction = new QAction(QIcon::fromTheme(QLatin1String("edit-copy")), tr("&Copy"), this);
1525     d->editCopyAction->setObjectName(QLatin1String("edit_copy"));
1526     d->editCopyAction->setToolTip(tr("Copy selection to clipboard"));
1527     d->editCopyAction->setShortcuts(KStandardShortcut::copy());
1528     d->editCopyAction->setProperty("iconOnly", true);
1529 
1530     d->editPasteAction = new QAction(QIcon::fromTheme(QLatin1String("edit-paste")), tr("&Paste"), this);
1531     d->editPasteAction->setObjectName(QLatin1String("edit_paste"));
1532     d->editPasteAction->setToolTip(tr("Paste clipboard content"));
1533     d->editPasteAction->setShortcuts(KStandardShortcut::paste());
1534     d->editPasteAction->setProperty("iconOnly", true);
1535 
1536     const KGuiItem del = KStandardGuiItem::del();
1537     d->editDeleteAction = new QAction(del.icon(), del.text(), this);
1538     d->editDeleteAction->setObjectName(QLatin1String("edit_delete"));
1539     d->editDeleteAction->setToolTip(del.toolTip());
1540     d->editDeleteAction->setWhatsThis(del.whatsThis());
1541     d->editDeleteAction->setProperty("iconOnly", true);
1542 
1543     d->sectionEdit = new QAction(tr("Edit Sections"), this);
1544     d->sectionEdit->setObjectName(QLatin1String("section_edit"));
1545 
1546     d->itemRaiseAction = new QAction(QIcon::fromTheme(QLatin1String("arrow-up")), tr("Raise"), this);
1547     d->itemRaiseAction->setObjectName(QLatin1String("item_raise"));
1548     d->itemLowerAction = new QAction(QIcon::fromTheme(QLatin1String("arrow-down")), tr("Lower"), this);
1549     d->itemLowerAction->setObjectName(QLatin1String("item_lower"));
1550 
1551     //Edit Actions
1552     connect(d->editCutAction, SIGNAL(triggered(bool)), this, SLOT(slotEditCut()));
1553     connect(d->editCopyAction, SIGNAL(triggered(bool)), this, SLOT(slotEditCopy()));
1554     connect(d->editPasteAction, SIGNAL(triggered(bool)), this, SLOT(slotEditPaste()));
1555     connect(d->editDeleteAction, SIGNAL(triggered(bool)), this, SLOT(slotEditDelete()));
1556 
1557     connect(d->sectionEdit, SIGNAL(triggered(bool)), this, SLOT(slotSectionEditor()));
1558 
1559     //Raise/Lower
1560     connect(d->itemRaiseAction, SIGNAL(triggered(bool)), this, SLOT(slotRaiseSelected()));
1561     connect(d->itemLowerAction, SIGNAL(triggered(bool)), this, SLOT(slotLowerSelected()));
1562 }
1563 
setSectionCursor(const QCursor & c)1564 void KReportDesigner::setSectionCursor(const QCursor& c)
1565 {
1566     if (d->pageFooterAny)
1567         d->pageFooterAny->setSectionCursor(c);
1568     if (d->pageFooterEven)
1569         d->pageFooterEven->setSectionCursor(c);
1570     if (d->pageFooterFirst)
1571         d->pageFooterFirst->setSectionCursor(c);
1572     if (d->pageFooterLast)
1573         d->pageFooterLast->setSectionCursor(c);
1574     if (d->pageFooterOdd)
1575         d->pageFooterOdd->setSectionCursor(c);
1576 
1577     if (d->pageHeaderAny)
1578         d->pageHeaderAny->setSectionCursor(c);
1579     if (d->pageHeaderEven)
1580         d->pageHeaderEven->setSectionCursor(c);
1581     if (d->pageHeaderFirst)
1582         d->pageHeaderFirst->setSectionCursor(c);
1583     if (d->pageHeaderLast)
1584         d->pageHeaderLast->setSectionCursor(c);
1585     if (d->pageHeaderOdd)
1586         d->pageHeaderOdd->setSectionCursor(c);
1587 
1588     if (d->detail)
1589         d->detail->setSectionCursor(c);
1590 }
1591 
unsetSectionCursor()1592 void KReportDesigner::unsetSectionCursor()
1593 {
1594     if (d->pageFooterAny)
1595         d->pageFooterAny->unsetSectionCursor();
1596     if (d->pageFooterEven)
1597         d->pageFooterEven->unsetSectionCursor();
1598     if (d->pageFooterFirst)
1599         d->pageFooterFirst->unsetSectionCursor();
1600     if (d->pageFooterLast)
1601         d->pageFooterLast->unsetSectionCursor();
1602     if (d->pageFooterOdd)
1603         d->pageFooterOdd->unsetSectionCursor();
1604 
1605     if (d->pageHeaderAny)
1606         d->pageHeaderAny->unsetSectionCursor();
1607     if (d->pageHeaderEven)
1608         d->pageHeaderEven->unsetSectionCursor();
1609     if (d->pageHeaderFirst)
1610         d->pageHeaderFirst->unsetSectionCursor();
1611     if (d->pageHeaderLast)
1612         d->pageHeaderLast->unsetSectionCursor();
1613     if (d->pageHeaderOdd)
1614         d->pageHeaderOdd->unsetSectionCursor();
1615 
1616     if (d->detail)
1617         d->detail->unsetSectionCursor();
1618 }
1619 
countSelectionHeight() const1620 qreal KReportDesigner::countSelectionHeight() const
1621 {
1622     if (d->releaseY == -1 || d->pressY == -1) {
1623         return -1;
1624     }
1625     return qAbs(d->releaseY - d->pressY);
1626 }
1627 
countSelectionWidth() const1628 qreal KReportDesigner::countSelectionWidth() const
1629 {
1630     if (d->releaseX == -1 || d->pressX == -1) {
1631         return -1;
1632     }
1633     return qAbs(d->releaseX - d->pressX);
1634 }
1635 
getSelectionPressX() const1636 qreal KReportDesigner::getSelectionPressX() const
1637 {
1638     return d->pressX;
1639 }
1640 
getSelectionPressY() const1641 qreal KReportDesigner::getSelectionPressY() const
1642 {
1643     return d->pressY;
1644 }
1645 
getPressPoint() const1646 QPointF KReportDesigner::getPressPoint() const
1647 {
1648     return QPointF(d->pressX, d->pressY);
1649 }
1650 
getReleasePoint() const1651 QPointF KReportDesigner::getReleasePoint() const
1652 {
1653     return QPointF(d->releaseX, d->releaseY);
1654 }
1655 
plugItemActions(const QList<QAction * > & actList)1656 void KReportDesigner::plugItemActions(const QList<QAction*> &actList)
1657 {
1658     foreach(QAction *a, actList) {
1659         connect(a, SIGNAL(triggered(bool)), this, SLOT(slotItemTriggered(bool)));
1660     }
1661 }
1662 
slotItemTriggered(bool checked)1663 void KReportDesigner::slotItemTriggered(bool checked)
1664 {
1665     if (!checked) {
1666         return;
1667     }
1668     QObject *theSender = sender();
1669     if (!theSender) {
1670         return;
1671     }
1672     slotItem(theSender->objectName());
1673 }
1674 
addMetaProperties(KPropertySet * set,const QString & classString,const QString & iconName)1675 void KReportDesigner::addMetaProperties(KPropertySet* set, const QString &classString,
1676                                         const QString &iconName)
1677 {
1678     Q_ASSERT(set);
1679     KProperty *prop;
1680     set->addProperty(prop = new KProperty("this:classString", classString));
1681     prop->setVisible(false);
1682     set->addProperty(prop = new KProperty("this:iconName", iconName));
1683     prop->setVisible(false);
1684 }
1685 
recalculateMaxMargins()1686 void KReportDesigner::recalculateMaxMargins()
1687 {
1688     QSize pageSize = pageSizePt();
1689     d->leftMargin->setOption("max", d->currentUnit.convertFromPoint(pageSize.width() - d->currentUnit.convertToPoint(d->rightMargin->value().toReal()) - SMALLEST_PAGE_SIZE_PT));
1690     d->rightMargin->setOption("max", d->currentUnit.convertFromPoint(pageSize.width()  - d->currentUnit.convertToPoint(d->leftMargin->value().toReal())- SMALLEST_PAGE_SIZE_PT));
1691     d->topMargin->setOption("max", d->currentUnit.convertFromPoint(pageSize.height()  - d->currentUnit.convertToPoint(d->bottomMargin->value().toReal())- SMALLEST_PAGE_SIZE_PT));
1692     d->bottomMargin->setOption("max", d->currentUnit.convertFromPoint(pageSize.height()  - d->currentUnit.convertToPoint(d->topMargin->value().toReal())- SMALLEST_PAGE_SIZE_PT));
1693 }
1694