diff --git a/kexi/core/KexiView.h b/kexi/core/KexiView.h --- a/kexi/core/KexiView.h +++ b/kexi/core/KexiView.h @@ -35,6 +35,7 @@ namespace KexiDB { +class Cursor; class SchemaData; } @@ -148,6 +149,9 @@ Q_DECLARE_FLAGS(StoreNewDataOptions, StoreNewDataOption) public Q_SLOTS: + /*! Sets data. Only works for db-aware view. */ + bool setData(KexiDB::Cursor *cursor); + virtual void setFocus(); /*! Call this in your view's implementation whenever current property set @@ -176,6 +180,11 @@ void focus(bool in); protected: + /*! Sets data to the widget. Default implementation just calls KexiDB::Cursor::open(). + Used internally by setData(KexiDB::Cursor*). + @return true on success. */ + virtual bool setWidgetData(KexiDB::Cursor *cursor); + virtual bool eventFilter(QObject *o, QEvent *e); /*! called by KexiWindow::switchToViewMode() right before window is switched to new mode diff --git a/kexi/core/KexiView.cpp b/kexi/core/KexiView.cpp --- a/kexi/core/KexiView.cpp +++ b/kexi/core/KexiView.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -555,6 +556,32 @@ window()->removeView(mode); } +bool KexiView::setWidgetData(KexiDB::Cursor *cursor) +{ + return cursor->open(); +} + +bool KexiView::setData(KexiDB::Cursor *cursor) +{ + bool ok = setWidgetData(cursor); + if (!ok && cursor) { + // A simple workaround needed because the cursor will be destroyed before + // the error message is built. Kexi 3 would have cleaner solution. + // See https://bugs.kde.org/show_bug.cgi?id=356888 + QString msg = cursor->errorMsg(); + QString desc; + if (!cursor->serverErrorMsg().isEmpty()) { + if (msg.isEmpty()) { + msg = cursor->serverErrorMsg(); + } else { + desc = cursor->serverErrorMsg(); + } + } + window()->setStatus(msg, desc, cursor->recentSQLString()); + } + return ok; +} + void KexiView::setFocus() { if (!d->lastFocusedChildBeforeFocusOut.isNull()) { diff --git a/kexi/core/kexi.h b/kexi/core/kexi.h --- a/kexi/core/kexi.h +++ b/kexi/core/kexi.h @@ -110,7 +110,8 @@ bool error() const; - void setStatus(const QString& message, const QString& description); + void setStatus(const QString& message, const QString& description, + const QString& errorSql = QString()); //! Note: for safety, \a dbObject needs to be derived from QObject, //! otherwise it won't be assigned @@ -137,7 +138,9 @@ //! by receiving a message operator KexiDB::MessageHandler*(); - QString message, description; + QString message; + QString description; + QString errorSql; //!< Recently executed SQL, overrides information from dbObject() protected: QPointer dbObj; //! This is in fact KexiDB::Object KexiDB::MessageHandler* msgHandler; diff --git a/kexi/core/kexi.cpp b/kexi/core/kexi.cpp --- a/kexi/core/kexi.cpp +++ b/kexi/core/kexi.cpp @@ -198,11 +198,12 @@ || (dynamic_cast((QObject*)dbObj) && dynamic_cast((QObject*)dbObj)->error()); } -void ObjectStatus::setStatus(const QString& message, const QString& description) +void ObjectStatus::setStatus(const QString& message, const QString& description, const QString& errorSql) { this->dbObj = 0; this->message = message; this->description = description; + this->errorSql = errorSql; } void ObjectStatus::setStatus(KexiDB::Object* dbObject, const QString& message, const QString& description) diff --git a/kexi/core/kexiproject.cpp b/kexi/core/kexiproject.cpp --- a/kexi/core/kexiproject.cpp +++ b/kexi/core/kexiproject.cpp @@ -861,10 +861,12 @@ return 0; KexiWindow *window = part->openInstance(parent, item, viewMode, staticObjectArgs); if (!window) { - if (part->lastOperationStatus().error()) + if (part->lastOperationStatus().error()) { + m_sql = part->lastOperationStatus().errorSql; // pass proper SQL error setError(i18n("Opening object \"%1\" failed.", item.name()) + "
" + part->lastOperationStatus().message, part->lastOperationStatus().description); + } return 0; } return window; diff --git a/kexi/plugins/forms/kexiformview.h b/kexi/plugins/forms/kexiformview.h --- a/kexi/plugins/forms/kexiformview.h +++ b/kexi/plugins/forms/kexiformview.h @@ -151,7 +151,7 @@ //! Redirects to Container::eventFilter(). virtual void contextMenuEvent(QContextMenuEvent *e); - void initDataSource(); + bool initDataSource(); virtual void setFocusInternal(); /*! Called after loading the form contents (before showing it). */ diff --git a/kexi/plugins/forms/kexiformview.cpp b/kexi/plugins/forms/kexiformview.cpp --- a/kexi/plugins/forms/kexiformview.cpp +++ b/kexi/plugins/forms/kexiformview.cpp @@ -540,7 +540,9 @@ if (viewMode() == Kexi::DataViewMode) { //TMP!! - initDataSource(); + if (!initDataSource()) { + return false; + } //handle events for this form d->scrollView->setMainWidgetForEventHandling(d->dbform); @@ -596,16 +598,20 @@ return dynamic_cast(part()); } -void KexiFormView::initDataSource() +bool KexiFormView::initDataSource() { deleteQuery(); + const QString dataSourceString(d->dbform->dataSource()); + if (dataSourceString.isEmpty()) { + return true; // nothing to do + } + //! @todo also handle anonymous (not stored) queries provided as statements here KexiDB::TableSchema *tableSchema = 0; KexiDB::Connection *conn = 0; QStringList sources; bool forceReadOnlyDataSource = false; - QString dataSourceString(d->dbform->dataSource()); - bool ok = !dataSourceString.isEmpty(); + bool ok = true; if (ok) { //collect all data-aware widgets and create query schema d->scrollView->setMainDataSourceWidget(d->dbform); @@ -700,11 +706,15 @@ KexiUtils::WaitCursorRemover remover; params = KexiQueryParameters::getParameters(this, *conn->driver(), *d->query, ok); } - if (ok) //input cancelled - d->cursor = conn->executeQuery(*d->query, params); + if (ok) { //input cancelled + d->cursor = conn->prepareQuery(*d->query, params); + } } d->scrollView->invalidateDataSources(invalidSources, d->query); ok = d->cursor != 0; + if (ok) { + ok = setData(d->cursor); + } } if (!invalidSources.isEmpty()) @@ -716,7 +726,7 @@ KexiDB::TableViewData* data = new KexiDB::TableViewData(d->cursor); if (forceReadOnlyDataSource) data->setReadOnly(true); - data->preloadAllRows(); + ok = data->preloadAllRows(); ///*! @todo few backends return result count for free! - no need to reopen() */ // int resultCount = -1; @@ -727,11 +737,16 @@ // if (ok) // ok = ! (!d->cursor->moveFirst() && d->cursor->error()); - d->scrollView->setData(data, true /*owner*/); + if (ok) { + d->scrollView->setData(data, true /*owner*/); + } else { + delete data; + } } else { d->scrollView->setData(0, false); } + return ok; } void KexiFormView::setFormModified() diff --git a/kexi/plugins/queries/kexiqueryview.cpp b/kexi/plugins/queries/kexiqueryview.cpp --- a/kexi/plugins/queries/kexiqueryview.cpp +++ b/kexi/plugins/queries/kexiqueryview.cpp @@ -88,21 +88,28 @@ if (!ok) {//input cancelled return cancelled; } - d->cursor = conn->executeQuery(*query, d->currentParams); + d->cursor = conn->prepareQuery(*query, d->currentParams); if (!d->cursor) { window()->setStatus( conn, i18n("Query executing failed.")); //! @todo also provide server result and sql statement return false; } - setData(d->cursor); + ok = setData(d->cursor); //! @todo remove close() when dynamic cursors arrive - d->cursor->close(); - - if (oldCursor) - oldCursor->connection()->deleteCursor(oldCursor); + if (!d->cursor->close()) { + ok = false; + } + if (oldCursor) { + conn->deleteCursor(oldCursor); + } + if (!ok) { + conn->deleteCursor(d->cursor); + d->cursor = 0; + return false; + } //! @todo maybe allow writing and inserting for single-table relations? tableView()->setReadOnly(true); diff --git a/kexi/plugins/reports/kexidbreportdata.h b/kexi/plugins/reports/kexidbreportdata.h --- a/kexi/plugins/reports/kexidbreportdata.h +++ b/kexi/plugins/reports/kexidbreportdata.h @@ -1,6 +1,7 @@ /* * Kexi Report Plugin * Copyright (C) 2007-2009 by Adam Pigg (adam@piggz.co.uk) +* Copyright (C) 2015 Jarosław Staniek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,22 +28,24 @@ #include +class KexiReportView; + /** */ class KexiDBReportData : public KoReportData { public: - KexiDBReportData(const QString &objectName, KexiDB::Connection *conn); + KexiDBReportData(const QString &objectName, KexiDB::Connection *conn, KexiReportView *view); /*! * @a partClass specifies @a objectName type: a table or query. * Types accepted: * -"org.kexi-project.table" * -"org.kexi-project.query" * -empty QString() - attempt to resolve @a objectName */ - KexiDBReportData(const QString &objectName, const QString& partClass, KexiDB::Connection *conn); + KexiDBReportData(const QString &objectName, const QString& partClass, KexiDB::Connection *conn, KexiReportView *view); virtual ~KexiDBReportData(); virtual QStringList fieldNames() const; diff --git a/kexi/plugins/reports/kexidbreportdata.cpp b/kexi/plugins/reports/kexidbreportdata.cpp --- a/kexi/plugins/reports/kexidbreportdata.cpp +++ b/kexi/plugins/reports/kexidbreportdata.cpp @@ -1,6 +1,7 @@ /* * Kexi Report Plugin * Copyright (C) 2007-2009 by Adam Pigg (adam@piggz.co.uk) +* Copyright (C) 2015 Jarosław Staniek * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,17 +18,19 @@ */ #include "kexidbreportdata.h" -#include +#include "kexireportview.h" #include #include -#include +#include + +#include class KexiDBReportData::Private { public: - explicit Private(KexiDB::Connection *pDb) - : cursor(0), connection(pDb), originalSchema(0), copySchema(0) + explicit Private(KexiDB::Connection *pDb, KexiReportView *v) + : cursor(0), connection(pDb), view(v), originalSchema(0), copySchema(0) { } ~Private() @@ -37,27 +40,26 @@ delete cursor; } - QString objectName; - KexiDB::Cursor *cursor; - KexiDB::Connection *connection; + KexiDB::Connection * const connection; + KexiReportView * const view; KexiDB::QuerySchema *originalSchema; KexiDB::QuerySchema *copySchema; }; KexiDBReportData::KexiDBReportData (const QString &objectName, - KexiDB::Connection * pDb) - : d(new Private(pDb)) + KexiDB::Connection * pDb, KexiReportView *view) + : d(new Private(pDb, view)) { d->objectName = objectName; getSchema(); } KexiDBReportData::KexiDBReportData(const QString& objectName, const QString& partClass, - KexiDB::Connection* pDb) - : d(new Private(pDb)) + KexiDB::Connection* pDb, KexiReportView *view) + : d(new Private(pDb, view)) { d->objectName = objectName; getSchema(partClass); @@ -102,22 +104,24 @@ { if ( d->objectName.isEmpty() ) { - d->cursor = d->connection->executeQuery ( "SELECT '' AS expr1 FROM kexi__db WHERE kexi__db.db_property = 'kexidb_major_ver'" ); + d->cursor = d->connection->prepareQuery("SELECT '' AS expr1 FROM kexi__db WHERE kexi__db.db_property = 'kexidb_major_ver'"); } else if ( d->copySchema) { kDebug() << "Opening cursor.." << d->copySchema->debugString(); - d->cursor = d->connection->executeQuery ( *d->copySchema, 1 ); + d->cursor = d->connection->prepareQuery(*d->copySchema, KexiDB::Cursor::Buffered); } - - if ( d->cursor ) - { - kDebug() << "Moving to first record.."; - return d->cursor->moveFirst(); + if (d->cursor) { + bool ok = d->view->setData(d->cursor); + if (ok) { + kDebug() << "Moving to first record.."; + if (!d->cursor->moveFirst()) { + ok = !d->cursor->error(); + } + } + return ok; } - else - return false; } return false; } @@ -396,5 +400,5 @@ KoReportData* KexiDBReportData::create(const QString& source) { - return new KexiDBReportData(source, d->connection); + return new KexiDBReportData(source, d->connection, d->view); } diff --git a/kexi/plugins/reports/kexireportview.h b/kexi/plugins/reports/kexireportview.h --- a/kexi/plugins/reports/kexireportview.h +++ b/kexi/plugins/reports/kexireportview.h @@ -62,7 +62,6 @@ private: KoReportPreRenderer *m_preRenderer; - ORODocument *m_reportDocument; QGraphicsView *m_reportView; QGraphicsScene *m_reportScene; KoReportPage *m_reportPage; diff --git a/kexi/plugins/reports/kexireportview.cpp b/kexi/plugins/reports/kexireportview.cpp --- a/kexi/plugins/reports/kexireportview.cpp +++ b/kexi/plugins/reports/kexireportview.cpp @@ -55,7 +55,7 @@ #include "../scripting/kexiscripting/kexiscriptadaptor.h" KexiReportView::KexiReportView(QWidget *parent) - : KexiView(parent), m_preRenderer(0), m_reportDocument(0), m_currentPage(0), m_pageCount(0), m_kexi(0), m_functions(0) + : KexiView(parent), m_preRenderer(0), m_currentPage(0), m_pageCount(0), m_kexi(0), m_functions(0) { setObjectName("KexiReportDesigner_DataView"); @@ -177,7 +177,6 @@ delete m_preRenderer; delete m_kexi; delete m_functions; - delete m_reportDocument; } void KexiReportView::slotPrintReport() @@ -193,7 +192,7 @@ cxt.printer = &printer; cxt.painter = &painter; - renderer->render(cxt, m_reportDocument); + renderer->render(cxt, m_preRenderer->document()); } delete dialog; delete renderer; @@ -223,7 +222,7 @@ painter.begin(&printer); cxt.printer = &printer; cxt.painter = &painter; - if (!renderer->render(cxt, m_reportDocument)) { + if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, i18n("Exporting the report as PDF to %1 failed.", cxt.destinationUrl.prettyUrl()), i18n("Export Failed")); @@ -299,7 +298,7 @@ return; } - if (!renderer->render(cxt, m_reportDocument)) { + if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, i18n("Failed to export the report as spreadsheet to %1.", cxt.destinationUrl.prettyUrl()), i18n("Export Failed")); @@ -325,7 +324,7 @@ return; } - if (!renderer->render(cxt, m_reportDocument)) { + if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, i18n("Exporting the report as text document to %1 failed.", cxt.destinationUrl.prettyUrl()), i18n("Export Failed")); @@ -366,7 +365,7 @@ renderer = m_factory.createInstance("htmltable"); } - if (!renderer->render(cxt, m_reportDocument)) { + if (!renderer->render(cxt, m_preRenderer->document())) { KMessageBox::error(this, i18n("Exporting the report as web page to %1 failed.", cxt.destinationUrl.prettyUrl()), i18n("Export Failed")); @@ -388,6 +387,8 @@ Q_UNUSED(mode); if (tempData()->reportSchemaChangedInPreviousView) { + tempData()->reportSchemaChangedInPreviousView = false; + kDebug() << "Schema changed"; delete m_preRenderer; @@ -399,7 +400,7 @@ reportData = sourceData(tempData()->connectionDefinition); } if (!reportData) { - reportData = new KexiDBReportData(QString(), KexiMainWindowIface::global()->project()->dbConnection()); + reportData = new KexiDBReportData(QString(), KexiMainWindowIface::global()->project()->dbConnection(), this); } m_preRenderer->setSourceData(reportData); @@ -419,30 +420,24 @@ m_preRenderer->registerScriptObject(m_functions, "field"); } - delete m_reportDocument; // prev document - m_reportDocument = m_preRenderer->generate(); - if (m_reportDocument) { - m_pageCount = m_reportDocument->pages(); + if (!m_preRenderer->generateDocument()) { + return false; + } + m_pageCount = m_preRenderer->document()->pages(); #ifndef KEXI_MOBILE - m_pageSelector->setRecordCount(m_pageCount); - m_pageSelector->setCurrentRecordNumber(1); + m_pageSelector->setRecordCount(m_pageCount); + m_pageSelector->setCurrentRecordNumber(1); #endif - } - - m_reportPage = new KoReportPage(this, m_reportDocument); + m_reportPage = new KoReportPage(this, m_preRenderer->document()); m_reportPage->setObjectName("KexiReportPage"); m_reportScene->setSceneRect(0,0,m_reportPage->rect().width() + 40, m_reportPage->rect().height() + 40); m_reportScene->addItem(m_reportPage); m_reportPage->setPos(20,20); m_reportView->centerOn(0,0); - } else { KMessageBox::error(this, i18n("Report schema appears to be invalid or corrupt"), i18n("Opening failed")); } - - - tempData()->reportSchemaChangedInPreviousView = false; } return true; } @@ -452,7 +447,9 @@ KoReportData *kodata = 0; if (e.attribute("type") == "internal") { - kodata = new KexiDBReportData(e.attribute("source"), KexiMainWindowIface::global()->project()->dbConnection()); + kodata = new KexiDBReportData(e.attribute("source"), + KexiMainWindowIface::global()->project()->dbConnection(), + this); } #ifndef KEXI_MOBILE if (e.attribute("type") == "external") { diff --git a/kexi/plugins/reports/kexisourceselector.cpp b/kexi/plugins/reports/kexisourceselector.cpp --- a/kexi/plugins/reports/kexisourceselector.cpp +++ b/kexi/plugins/reports/kexisourceselector.cpp @@ -17,16 +17,19 @@ */ #include "kexisourceselector.h" +#include "kexireportview.h" +#include +#include "KexiDataSourceComboBox.h" +#include +#include #include #include - -#include #include #include + #include -#include "KexiDataSourceComboBox.h" -#include +#include //#define NO_EXTERNAL_SOURCES @@ -178,8 +181,17 @@ //!@TODO Fix when enable external data #ifndef NO_EXTERNAL_SOURCES + KexiReportView *view = 0; + if (KexiMainWindowIface::global()->currentWindow()) { + view = qobject_cast(KexiMainWindowIface::global()->currentWindow()->selectedView()); + if (!view) { + return 0; + } + } if (d->sourceType->itemData(d->sourceType->currentIndex()).toString() == "internal" && d->internalSource->isSelectionValid()) { - d->kexiDBData = new KexiDBReportData(d->internalSource->selectedName(), d->internalSource->selectedPartClass(), d->conn); + d->kexiDBData = new KexiDBReportData(d->internalSource->selectedName(), + d->internalSource->selectedPartClass(), + d->conn, view); return d->kexiDBData; } diff --git a/kexi/plugins/tables/kexitabledesigner_dataview.cpp b/kexi/plugins/tables/kexitabledesigner_dataview.cpp --- a/kexi/plugins/tables/kexitabledesigner_dataview.cpp +++ b/kexi/plugins/tables/kexitabledesigner_dataview.cpp @@ -76,12 +76,15 @@ if (tempData()->tableSchemaChangedInPreviousView) { KexiUtils::WaitCursor wait; - KexiDB::Cursor *c - = KexiMainWindowIface::global()->project()->dbConnection()->prepareQuery( - *tempData()->table); - if (!c) + KexiDB::Connection *conn = KexiMainWindowIface::global()->project()->dbConnection(); + KexiDB::Cursor *cursor = conn->prepareQuery(*tempData()->table); + if (!cursor) { return false; - setData(c); + } + if (!setData(cursor)) { + conn->deleteCursor(cursor); + return false; + } tempData()->tableSchemaChangedInPreviousView = false; } return true; diff --git a/kexi/widget/tableview/KexiDataTableScrollArea.cpp b/kexi/widget/tableview/KexiDataTableScrollArea.cpp --- a/kexi/widget/tableview/KexiDataTableScrollArea.cpp +++ b/kexi/widget/tableview/KexiDataTableScrollArea.cpp @@ -70,6 +70,7 @@ kWarning() << "Cursor should have query schema defined!\n--aborting setData().\n"; m_cursor->debug(); clearColumns(); + m_cursor = 0; return false; } @@ -82,6 +83,7 @@ kWarning() << "Cannot open cursor\n--aborting setData(). \n" << m_cursor->serverErrorMsg(); m_cursor->debug(); clearColumns(); + m_cursor = 0; return false; } @@ -98,8 +100,11 @@ setWindowTitle(windowTitle); //PRIMITIVE!! data setting: - tv_data->preloadAllRows(); - + if (!tv_data->preloadAllRows()) { + delete tv_data; + clearColumns(); + m_cursor = 0; + } KexiTableScrollArea::setData(tv_data); return true; } diff --git a/kexi/widget/tableview/KexiDataTableView.h b/kexi/widget/tableview/KexiDataTableView.h --- a/kexi/widget/tableview/KexiDataTableView.h +++ b/kexi/widget/tableview/KexiDataTableView.h @@ -66,9 +66,6 @@ bool loadTableViewSettings(KexiDB::TableViewData* data); public Q_SLOTS: - /*! Sets data. Only works for db-aware table. */ - void setData(KexiDB::Cursor *cursor); - /*! Saves settings for the view. Implemented for KexiView. */ virtual bool saveSettings(); @@ -79,6 +76,8 @@ protected: void init(); + virtual bool setWidgetData(KexiDB::Cursor *cursor); + class Private; Private * const d; }; diff --git a/kexi/widget/tableview/KexiDataTableView.cpp b/kexi/widget/tableview/KexiDataTableView.cpp --- a/kexi/widget/tableview/KexiDataTableView.cpp +++ b/kexi/widget/tableview/KexiDataTableView.cpp @@ -110,12 +110,13 @@ return true; } -void -KexiDataTableView::setData(KexiDB::Cursor *c) +bool KexiDataTableView::setWidgetData(KexiDB::Cursor *cursor) { - if (!dynamic_cast(mainWidget())) - return; - dynamic_cast(mainWidget())->setData(c); + bool ok = dynamic_cast(mainWidget()); + if (!ok) { + return false; + } + return dynamic_cast(mainWidget())->setData(cursor); } void KexiDataTableView::filter() diff --git a/libs/koreport/renderer/KoReportPage.cpp b/libs/koreport/renderer/KoReportPage.cpp --- a/libs/koreport/renderer/KoReportPage.cpp +++ b/libs/koreport/renderer/KoReportPage.cpp @@ -31,38 +31,33 @@ KoReportPage::KoReportPage(QWidget *parent, ORODocument *document) : QObject(parent), QGraphicsRectItem() + , m_reportDocument(document) + , m_page(0) { - //TODO setAttribute(Qt::WA_NoBackground); - //kDebug() << "CREATED PAGE"; - m_reportDocument = document; - m_page = 0; - int pageWidth = 0; - int pageHeight = 0; + Q_ASSERT(m_reportDocument); - if (m_reportDocument) { - QString pageSize = m_reportDocument->pageOptions().getPageSize(); - - - if (pageSize == "Custom") { - // if this is custom sized sheet of paper we will just use those values - pageWidth = (int)(m_reportDocument->pageOptions().getCustomWidth()); - pageHeight = (int)(m_reportDocument->pageOptions().getCustomHeight()); - } else { - // lookup the correct size information for the specified size paper - pageWidth = m_reportDocument->pageOptions().widthPx(); - pageHeight = m_reportDocument->pageOptions().heightPx(); - } + int pageWidth; + int pageHeight; + const QString pageSize = m_reportDocument->pageOptions().getPageSize(); + if (pageSize == "Custom") { + // if this is custom sized sheet of paper we will just use those values + pageWidth = (int)(m_reportDocument->pageOptions().getCustomWidth()); + pageHeight = (int)(m_reportDocument->pageOptions().getCustomHeight()); + } else { + // lookup the correct size information for the specified size paper + pageWidth = m_reportDocument->pageOptions().widthPx(); + pageHeight = m_reportDocument->pageOptions().heightPx(); } - setRect(0,0,pageWidth, pageHeight); - //kDebug() << "PAGE IS " << pageWidth << "x" << pageHeight; + setRect(0, 0, pageWidth, pageHeight); + m_pixmap = new QPixmap(pageWidth, pageHeight); m_renderer = m_factory.createInstance("screen"); connect(m_reportDocument, SIGNAL(updated(int)), this, SLOT(pageUpdated(int))); m_renderTimer = new QTimer(this); m_renderTimer->setSingleShot(true); connect(m_renderTimer, SIGNAL(timeout()), this, SLOT(renderCurrentPage())); - + renderPage(1); } @@ -107,5 +102,4 @@ renderPage(m_page + 1); } - #include "KoReportPage.moc" diff --git a/libs/koreport/renderer/KoReportPreRenderer.h b/libs/koreport/renderer/KoReportPreRenderer.h --- a/libs/koreport/renderer/KoReportPreRenderer.h +++ b/libs/koreport/renderer/KoReportPreRenderer.h @@ -46,8 +46,10 @@ void setSourceData(KoReportData*); void registerScriptObject(QObject*, const QString&); - - ORODocument * generate(); + + bool generateDocument(); + + ORODocument *document(); /** @brief Set the name of the report so that it can be used internally by the script engine @@ -63,7 +65,6 @@ private: KoReportPreRendererPrivate *const d; bool setDom(const QDomElement &); - QMap m_scriptObjects; }; #endif // __KOREPORTPRERENDERER_H__ diff --git a/libs/koreport/renderer/KoReportPreRenderer.cpp b/libs/koreport/renderer/KoReportPreRenderer.cpp --- a/libs/koreport/renderer/KoReportPreRenderer.cpp +++ b/libs/koreport/renderer/KoReportPreRenderer.cpp @@ -79,7 +79,12 @@ KRScriptHandler *m_scriptHandler; void initEngine(); + //! Generates m_document. Returns true on success. + //! @note m_document is not removed on failure, caller should remove it. + bool generateDocument(); + KoReportASyncItemManager* asyncManager; + QMap scriptObjects; private slots: void asyncItemsFinished(); @@ -113,6 +118,7 @@ m_reportData = 0; m_postProcText.clear(); + delete m_document; } void KoReportPreRendererPrivate::createNewPage() @@ -437,124 +443,107 @@ delete asyncManager; } - -//===========================KoReportPreRenderer=============================== - -KoReportPreRenderer::KoReportPreRenderer(const QDomElement & pDocument) : d(new KoReportPreRendererPrivate()) -{ - setDom(pDocument); -} - -KoReportPreRenderer::~KoReportPreRenderer() -{ - delete d; -} - -void KoReportPreRenderer::setName(const QString &n) +bool KoReportPreRendererPrivate::generateDocument() { - d->m_reportData->setName(n); -} - -ORODocument* KoReportPreRenderer::generate() -{ - //kDebug(); - if (d == 0 || !d->m_valid || d->m_reportData == 0 || d->m_kodata == 0) - return 0; - + if (!m_valid || !m_reportData || !m_kodata) { + return false; + } // Do this check now so we don't have to undo a lot of work later if it fails LabelSizeInfo label; - if (d->m_reportData->page.getPageSize() == "Labels") { - label = LabelSizeInfo::find(d->m_reportData->page.getLabelType()); - if (label.isNull()) - return 0; + if (m_reportData->page.getPageSize() == "Labels") { + label = LabelSizeInfo::find(m_reportData->page.getLabelType()); + if (label.isNull()) { + return false; + } } - //kDebug() << "Creating Document"; - d->m_document = new ORODocument(d->m_reportData->m_title); - - d->m_pageCounter = 0; - d->m_yOffset = 0.0; + m_document = new ORODocument(m_reportData->m_title); + m_pageCounter = 0; + m_yOffset = 0.0; //kDebug() << "Calculating Margins"; if (!label.isNull()) { - if (d->m_reportData->page.isPortrait()) { - d->m_topMargin = (label.startY() / 100.0); - d->m_bottomMargin = 0; - d->m_rightMargin = 0; - d->m_leftMargin = (label.startX() / 100.0); + if (m_reportData->page.isPortrait()) { + m_topMargin = (label.startY() / 100.0); + m_bottomMargin = 0; + m_rightMargin = 0; + m_leftMargin = (label.startX() / 100.0); } else { - d->m_topMargin = (label.startX() / 100.0); - d->m_bottomMargin = 0; - d->m_rightMargin = 0; - d->m_leftMargin = (label.startY() / 100.0); + m_topMargin = (label.startX() / 100.0); + m_bottomMargin = 0; + m_rightMargin = 0; + m_leftMargin = (label.startY() / 100.0); } } else { - d->m_topMargin = d->m_reportData->page.getMarginTop(); - d->m_bottomMargin = d->m_reportData->page.getMarginBottom(); - d->m_rightMargin = d->m_reportData->page.getMarginRight(); - d->m_leftMargin = d->m_reportData->page.getMarginLeft(); - //kDebug() << "Margins:" << d->m_topMargin << d->m_bottomMargin << d->m_rightMargin << d->m_leftMargin; + m_topMargin = m_reportData->page.getMarginTop(); + m_bottomMargin = m_reportData->page.getMarginBottom(); + m_rightMargin = m_reportData->page.getMarginRight(); + m_leftMargin = m_reportData->page.getMarginLeft(); + //kDebug() << "Margins:" << m_topMargin << m_bottomMargin << m_rightMargin << m_leftMargin; } //kDebug() << "Calculating Page Size"; - ReportPageOptions rpo(d->m_reportData->page); + ReportPageOptions rpo(m_reportData->page); // This should reflect the information of the report page size - if (d->m_reportData->page.getPageSize() == "Custom") { - d->m_maxWidth = d->m_reportData->page.getCustomWidth(); - d->m_maxHeight = d->m_reportData->page.getCustomHeight(); + if (m_reportData->page.getPageSize() == "Custom") { + m_maxWidth = m_reportData->page.getCustomWidth(); + m_maxHeight = m_reportData->page.getCustomHeight(); } else { if (!label.isNull()) { - d->m_maxWidth = label.width(); - d->m_maxHeight = label.height(); + m_maxWidth = label.width(); + m_maxHeight = label.height(); rpo.setPageSize(label.paper()); } else { // lookup the correct size information for the specified size paper - d->m_maxWidth = KoPageFormat::width(KoPageFormat::formatFromString(d->m_reportData->page.getPageSize()), KoPageFormat::Portrait); - d->m_maxHeight = KoPageFormat::height(KoPageFormat::formatFromString(d->m_reportData->page.getPageSize()), KoPageFormat::Portrait); + m_maxWidth = KoPageFormat::width(KoPageFormat::formatFromString(m_reportData->page.getPageSize()), KoPageFormat::Portrait); + m_maxHeight = KoPageFormat::height(KoPageFormat::formatFromString(m_reportData->page.getPageSize()), KoPageFormat::Portrait); KoUnit pageUnit(KoUnit::Millimeter); - d->m_maxWidth = KoUnit::toInch(pageUnit.fromUserValue(d->m_maxWidth)) * KoDpi::dpiX(); - d->m_maxHeight = KoUnit::toInch(pageUnit.fromUserValue(d->m_maxHeight)) * KoDpi::dpiY(); + m_maxWidth = KoUnit::toInch(pageUnit.fromUserValue(m_maxWidth)) * KoDpi::dpiX(); + m_maxHeight = KoUnit::toInch(pageUnit.fromUserValue(m_maxHeight)) * KoDpi::dpiY(); } } - if (!d->m_reportData->page.isPortrait()) { - qreal tmp = d->m_maxWidth; - d->m_maxWidth = d->m_maxHeight; - d->m_maxHeight = tmp; + if (!m_reportData->page.isPortrait()) { + qreal tmp = m_maxWidth; + m_maxWidth = m_maxHeight; + m_maxHeight = tmp; } - //kDebug() << "Page Size:" << d->m_maxWidth << d->m_maxHeight; + //kDebug() << "Page Size:" << m_maxWidth << m_maxHeight; - d->m_document->setPageOptions(rpo); - d->m_kodata->setSorting(d->m_reportData->m_detailSection->m_sortedFields); - d->m_kodata->open(); - d->initEngine(); + m_document->setPageOptions(rpo); + m_kodata->setSorting(m_reportData->m_detailSection->m_sortedFields); + if (!m_kodata->open()) { + return false; + } + initEngine(); //Loop through all abjects that have been registered, and register them with the script handler - if (d->m_scriptHandler) { - QMapIterator i(m_scriptObjects); + if (m_scriptHandler) { + QMapIterator i(scriptObjects); while (i.hasNext()) { i.next(); - d->m_scriptHandler->registerScriptObject(i.value(), i.key()); + m_scriptHandler->registerScriptObject(i.value(), i.key()); //!TODO This is a hack - if (i.key() == "field") - QObject::connect(d->m_scriptHandler, SIGNAL(groupChanged(QString)), i.value(), SLOT(setWhere(QString))); + if (i.key() == "field") { + QObject::connect(m_scriptHandler, SIGNAL(groupChanged(QString)), i.value(), SLOT(setWhere(QString))); + } } } //execute the script - d->m_scriptHandler->trigger(); + m_scriptHandler->trigger(); - d->createNewPage(); + createNewPage(); if (!label.isNull()) { // Label Print Run // remember the initial margin setting as we will be modifying // the value and restoring it as we move around - qreal margin = d->m_leftMargin; + qreal margin = m_leftMargin; - d->m_yOffset = d->m_topMargin; + m_yOffset = m_topMargin; qreal w = (label.width() / 100.0); qreal wg = (label.xGap() / 100.0); @@ -565,85 +554,120 @@ qreal tmp; // flip the value around if we are printing landscape - if (!d->m_reportData->page.isPortrait()) { + if (!m_reportData->page.isPortrait()) { w = (label.height() / 100.0); wg = (label.yGap() / 100.0); h = (label.width() / 100.0); hg = (label.xGap() / 100.0); numCols = label.rows(); numRows = label.columns(); } - KRDetailSectionData * detailData = d->m_reportData->m_detailSection; + KRDetailSectionData * detailData = m_reportData->m_detailSection; if (detailData->m_detailSection) { - KoReportData *mydata = d->m_kodata; + KoReportData *mydata = m_kodata; if (mydata && mydata->recordCount() > 0) { /* && !((query = orqThis->getQuery())->eof()))*/ - mydata->moveFirst(); + if (!mydata->moveFirst()) { + return false; + } int row = 0; int col = 0; do { - tmp = d->m_yOffset; // store the value as renderSection changes it - d->renderSection(*(detailData->m_detailSection)); - d->m_yOffset = tmp; // restore the value that renderSection modified + tmp = m_yOffset; // store the value as renderSection changes it + renderSection(*detailData->m_detailSection); + m_yOffset = tmp; // restore the value that renderSection modified col++; - d->m_leftMargin += w + wg; + m_leftMargin += w + wg; if (col >= numCols) { - d->m_leftMargin = margin; // reset back to original value + m_leftMargin = margin; // reset back to original value col = 0; row++; - d->m_yOffset += h + hg; + m_yOffset += h + hg; if (row >= numRows) { - d->m_yOffset = d->m_topMargin; + m_yOffset = m_topMargin; row = 0; - d->createNewPage(); + createNewPage(); } } } while (mydata->moveNext()); } } } else { // Normal Print Run - if (d->m_reportData->m_reportHeader) { - d->renderSection(*(d->m_reportData->m_reportHeader)); + if (m_reportData->m_reportHeader) { + renderSection(*m_reportData->m_reportHeader); } - if (d->m_reportData->m_detailSection) { - d->renderDetailSection(*(d->m_reportData->m_detailSection)); + if (m_reportData->m_detailSection) { + renderDetailSection(*m_reportData->m_detailSection); } - if (d->m_reportData->m_reportFooter) { - if (d->renderSectionSize(*(d->m_reportData->m_reportFooter)) + d->finishCurPageSize(true) + d->m_bottomMargin + d->m_yOffset >= d->m_maxHeight) { - d->createNewPage(); + if (m_reportData->m_reportFooter) { + if (renderSectionSize(*m_reportData->m_reportFooter) + finishCurPageSize(true) + m_bottomMargin + m_yOffset >= m_maxHeight) { + createNewPage(); } - d->renderSection(*(d->m_reportData->m_reportFooter)); + renderSection(*m_reportData->m_reportFooter); } } - d->finishCurPage(true); + finishCurPage(true); // _postProcText contains those text boxes that need to be updated // with information that wasn't available at the time it was added to the document - d->m_scriptHandler->setPageTotal(d->m_document->pages()); + m_scriptHandler->setPageTotal(m_document->pages()); - for (int i = 0; i < d->m_postProcText.size(); i++) { - OROTextBox * tb = d->m_postProcText.at(i); + for (int i = 0; i < m_postProcText.size(); i++) { + OROTextBox * tb = m_postProcText.at(i); - d->m_scriptHandler->setPageNumber(tb->page()->page() + 1); + m_scriptHandler->setPageNumber(tb->page()->page() + 1); - tb->setText(d->m_scriptHandler->evaluate(tb->text()).toString()); + tb->setText(m_scriptHandler->evaluate(tb->text()).toString()); } - d->asyncManager->startRendering(); + asyncManager->startRendering(); + + m_scriptHandler->displayErrors(); - d->m_scriptHandler->displayErrors(); + if (!m_kodata->close()) { + return false; + } + delete m_scriptHandler; + delete m_kodata; + m_postProcText.clear(); + return true; +} + +//===========================KoReportPreRenderer=============================== - d->m_kodata->close(); - delete d->m_scriptHandler; - delete d->m_kodata; - d->m_postProcText.clear(); +KoReportPreRenderer::KoReportPreRenderer(const QDomElement & pDocument) : d(new KoReportPreRendererPrivate()) +{ + setDom(pDocument); +} +KoReportPreRenderer::~KoReportPreRenderer() +{ + delete d; +} + +void KoReportPreRenderer::setName(const QString &n) +{ + d->m_reportData->setName(n); +} + +ORODocument* KoReportPreRenderer::document() +{ + return d->m_document; +} + +bool KoReportPreRenderer::generateDocument() +{ + delete d->m_document; + if (!d->generateDocument()) { + delete d->m_document; + d->m_document = 0; + } return d->m_document; } @@ -681,7 +705,7 @@ void KoReportPreRenderer::registerScriptObject(QObject* obj, const QString& name) { //kDebug() << name; - m_scriptObjects[name] = obj; + d->scriptObjects[name] = obj; } const KoReportReportData* KoReportPreRenderer::reportData() const diff --git a/libs/koreport/tests/KoReportTest.cpp b/libs/koreport/tests/KoReportTest.cpp --- a/libs/koreport/tests/KoReportTest.cpp +++ b/libs/koreport/tests/KoReportTest.cpp @@ -69,7 +69,7 @@ QCOMPARE(designer.propertySet()->property("margin-right").value().toDouble(), KoUnit::parseValue("4.0cm")); KoReportPreRenderer renderer( designer.document() ); - renderer.generate(); + QVERIFY(renderer.generateDocument()); ReportPageOptions opt = renderer.reportData()->pageOptions(); QCOMPARE(opt.getPageSize(), QString("A5")); @@ -118,7 +118,7 @@ QCOMPARE(end.toPoint(), QPointF(KoUnit::parseValue("4.5cm"), KoUnit::parseValue("2.5cm"))); KoReportPreRenderer renderer( designer.document() ); - renderer.generate(); + QVERIFY(renderer.generateDocument()); l = dynamic_cast(renderer.reportData()->object("line1")); QVERIFY(l != 0); @@ -171,7 +171,7 @@ QCOMPARE(rect->pointRect(), expected); KoReportPreRenderer renderer( designer.document() ); - renderer.generate(); + QVERIFY(renderer.generateDocument()); KoReportItemBase *item = dynamic_cast(renderer.reportData()->object("label1")); QVERIFY(item != 0); diff --git a/plan/libs/ui/reports/reportview.h b/plan/libs/ui/reports/reportview.h --- a/plan/libs/ui/reports/reportview.h +++ b/plan/libs/ui/reports/reportview.h @@ -196,7 +196,6 @@ private: KoReportPreRenderer *m_preRenderer; KoReportRendererFactory m_factory; - ORODocument *m_reportDocument; QGraphicsView *m_reportView; QGraphicsScene *m_reportScene; KoReportPage *m_reportPage; diff --git a/plan/libs/ui/reports/reportview.cpp b/plan/libs/ui/reports/reportview.cpp --- a/plan/libs/ui/reports/reportview.cpp +++ b/plan/libs/ui/reports/reportview.cpp @@ -338,7 +338,7 @@ KoPrintJob *ReportWidget::createPrintJob() { - return new ReportPrintingDialog( this, m_reportDocument ); + return new ReportPrintingDialog(this, m_preRenderer->document()); } void ReportWidget::slotExport() @@ -362,7 +362,7 @@ KoPageLayout ReportWidget::pageLayout() const { KoPageLayout p = ViewBase::pageLayout(); - ReportPageOptions opt = m_reportDocument->pageOptions(); + ReportPageOptions opt = m_preRenderer->document()->pageOptions(); p.orientation = opt.isPortrait() ? KoPageFormat::Portrait : KoPageFormat::Landscape; if (opt.getPageSize().isEmpty()) { @@ -420,7 +420,7 @@ kError()<<"Cannot create odt (table) renderer"; return; } - if (!renderer->render(context, m_reportDocument)) { + if (!renderer->render(context, m_preRenderer->document())) { KMessageBox::error(this, i18nc( "@info", "Failed to export to %1", context.destinationUrl.prettyUrl()) , i18n("Export to text document failed")); } } @@ -433,7 +433,7 @@ kError()<<"Cannot create odt (frames) renderer"; return; } - if (!renderer->render(context, m_reportDocument)) { + if (!renderer->render(context, m_preRenderer->document())) { KMessageBox::error(this, i18nc( "@info", "Failed to export to %1", context.destinationUrl.prettyUrl()) , i18n("Export to text document failed")); } } @@ -447,7 +447,7 @@ kError()<<"Cannot create ods renderer"; return; } - if (!renderer->render(context, m_reportDocument)) { + if (!renderer->render(context, m_preRenderer->document())) { KMessageBox::error(this, i18nc( "@info", "Failed to export to %1", context.destinationUrl.prettyUrl()) , i18n("Export to spreadsheet failed")); } } @@ -461,7 +461,7 @@ kError()<<"Cannot create html renderer"; return; } - if (!renderer->render(context, m_reportDocument)) { + if (!renderer->render(context, m_preRenderer->document())) { KMessageBox::error(this, i18nc( "@info", "Failed to export to %1", context.destinationUrl.prettyUrl()) , i18n("Export to HTML failed")); } } @@ -475,7 +475,7 @@ kError()<<"Cannot create xhtml css renderer"; return; } - if (!renderer->render(context, m_reportDocument)) { + if (!renderer->render(context, m_preRenderer->document())) { KMessageBox::error(this, i18nc( "@info", "Failed to export to %1", context.destinationUrl.prettyUrl()) , i18n("Export to XHTML failed")); } } @@ -524,11 +524,11 @@ m_preRenderer->setSourceData( rd ); m_preRenderer->registerScriptObject(new ProjectAccess( rd ), "project"); - m_reportDocument = m_preRenderer->generate(); - m_pageSelector->setMaximum( m_reportDocument ? m_reportDocument->pages() : 1 ); + const bool generated = m_preRenderer->generateDocument(); + m_pageSelector->setMaximum(generated ? m_preRenderer->document()->pages() : 1); m_pageSelector->setCurrentPage( 1 ); - m_reportPage = new KoReportPage(this, m_reportDocument); + m_reportPage = new KoReportPage(this, m_preRenderer->document()); m_reportPage->setObjectName("ReportPage"); m_reportScene->setSceneRect(0,0,m_reportPage->rect().width() + 40, m_reportPage->rect().height() + 40);