Commit ea40faaa authored by Thomas Zander's avatar Thomas Zander

New class; a basic printing dialog. Well, its a lot more than a dialog (and

really a crappy dialog at the moment, not even a canvas button!).
Current features are;
* Multi-threaded printing so while printing the canvas is fully responsive.
* Do the actual painting code in the main-thread to avoid locking problems.
* Nice progress reporting using the KoProgressUpdater.

This multi-threaded printing is only really useful for multi-page documents,
I'll adjust KWord to use it and I'm sure that KoPageApp can inherit from
this one to implement printing there with ease.

svn path=/trunk/koffice/; revision=689686
parent 33ba042e
......@@ -58,6 +58,8 @@ set(koguiutils_LIB_SRCS
KoColorPatch.cpp
KoDualColorButton.cpp
KoLineStyleSelector.cpp
KoProgressUpdater.cpp
KoPrintingDialog.cpp
)
kde4_add_ui_files( koguiutils_LIB_SRCS
......
/* This file is part of the KDE project
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoPrintingDialog.h"
#include "KoProgressUpdater.h"
#include <KoAction.h>
#include <KoZoomHandler.h>
#include <KoShapeManager.h>
// #include <KDebug>
#include <KLocale>
#include <QPainter>
#include <QPrinter>
#include <QGridLayout>
#include <QLabel>
#include <QProgressBar>
class KoPrintingDialog::Private {
public:
Private(KoPrintingDialog *dia)
: parent(dia),
stop(false),
shapeManager(0),
painter(0),
printer(new QPrinter()),
index(0),
progress(0)
{
action = new KoAction(parent);
QObject::connect(action, SIGNAL(triggered (const QVariant&)),
parent, SLOT(preparePage(const QVariant&)), Qt::DirectConnection);
QObject::connect(action, SIGNAL(updateUi (const QVariant&)),
parent, SLOT(printPage(const QVariant&)), Qt::DirectConnection);
}
~Private() {
delete printer;
}
void preparePage(const QVariant &page) {
const int pageNumber = page.toInt();
KoUpdater updater = updaters.at(index-1);
painter->save();
if(! stop)
parent->preparePage(pageNumber);
updater.setProgress(50);
// here we should create a set of progress objects and give one
// to each of the KoSHape::waitUntilReady() objects; calling the 'finish()' when done.
QList<KoShape*> shapes = parent->shapesOnPage(pageNumber);
const int progressPart = 50 / shapes.count();
foreach(KoShape *shape, shapes) {
; // shape->waitUntilReady();
updater.setProgress(updater.progress() + progressPart);
}
updater.setProgress(100);
}
void printPage(const QVariant &page) {
const int pageNumber = page.toInt();
if(! stop)
shapeManager->paint( *painter, zoomer, true );
painter->restore(); // matching the one in preparePage above
if(!stop && index < pages.count()) {
printer->newPage();
action->execute(pages[index++]);
return;
}
// printing done!
painter->end();
progress->cancel();
parent->printingDone();
parent->done(0);
}
void cancelPressed() {
stop = true;
progress->cancel();
}
KoZoomHandler zoomer;
KoAction *action;
KoPrintingDialog *parent;
bool stop;
KoShapeManager *shapeManager;
QPainter *painter;
QPrinter *printer;
int index; // index in the pages list.
KoProgressUpdater *progress;
QLabel *pageNumber;
QList<int> pages;
QList<KoUpdater> updaters;
};
KoPrintingDialog::KoPrintingDialog(QWidget *parent)
: QDialog(parent),
d(new Private(this))
{
setModal(true);
setAttribute(Qt::WA_DeleteOnClose, true);
QGridLayout *grid = new QGridLayout(this);
setLayout(grid);
QLabel *label = new QLabel(i18n("Printing"), this);
grid->addWidget(label, 0, 0);
d->pageNumber = new QLabel(this);
grid->addWidget(label, 1, 0);
QProgressBar *bar = new QProgressBar(this);
grid->addWidget(bar, 0, 1, 2, 1);
d->progress = new KoProgressUpdater(bar);
}
KoPrintingDialog::~KoPrintingDialog()
{
delete d;
}
void KoPrintingDialog::setShapeManager(KoShapeManager *sm) {
d->shapeManager = sm;
}
void KoPrintingDialog::setPageRange(const QList<int> &pages) {
if(d->index == 0) // can't change after we started
d->pages = pages;
}
KoShapeManager *KoPrintingDialog::shapeManager() const {
return d->shapeManager;
}
QPainter & KoPrintingDialog::painter() const {
Q_ASSERT(d->painter);
return *d->painter;
}
bool KoPrintingDialog::isCancelled() const {
return d->stop;
}
void KoPrintingDialog::showEvent(QShowEvent *event) {
QWidget::showEvent(event);
if(d->index == 0 && d->pages.count() > 0 && d->printer) {
d->painter = new QPainter(d->printer);
d->zoomer.setZoomAndResolution(100, d->printer->resolution(), d->printer->resolution());
d->progress->start();
for(int i=0; i < d->pages.count(); i++)
d->updaters.append(d->progress->startSubtask()); // one per page
d->action->execute(d->pages[d->index++]);
}
}
QPrinter &KoPrintingDialog::printer() {
return *d->printer;
}
#include <KoPrintingDialog.moc>
/* This file is part of the KDE project
* Copyright (C) 2007 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOPRINTINGDIALOG_H
#define KOPRINTINGDIALOG_H
#include "koguiutils_export.h"
#include <QDialog>
#include <QList>
class KoShapeManager;
class KoShape;
class KOGUIUTILS_EXPORT KoPrintingDialog : public QDialog {
Q_OBJECT
public:
KoPrintingDialog(QWidget *parent);
virtual ~KoPrintingDialog();
void setShapeManager(KoShapeManager *sm);
void setPageRange(const QList<int> &pages);
QPrinter &printer();
protected:
virtual void preparePage(int pageNumber) = 0;
virtual QList<KoShape*> shapesOnPage(int pageNumber) = 0;
KoShapeManager *shapeManager() const;
QPainter &painter() const;
bool isCancelled() const;
/// reimplemented
virtual void showEvent(QShowEvent *event);
virtual void printingDone() { }
private:
class Private;
Private * const d;
Q_PRIVATE_SLOT(d, void preparePage(const QVariant &page))
Q_PRIVATE_SLOT(d, void printPage(const QVariant &page))
Q_PRIVATE_SLOT(d, void cancelPressed())
};
#endif
/* This file is part of the KDE project
* Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoProgressUpdater.h"
#include <threadAction/KoAction.h>
#include <threadAction/KoExecutePolicy.h>
#include <threadweaver/ThreadWeaver.h>
#include <QProgressBar>
#include <QString>
class KoProgressUpdaterPrivate : public QObject {
public:
KoProgressUpdaterPrivate(KoProgressUpdater *parent, int weight)
: m_progress(0),
m_weight(weight),
m_interrupted(false),
m_parent(parent)
{
}
bool interrupted() const { return m_interrupted; }
int progress() const { return m_progress; }
int weight() const { return m_weight; }
void cancel() {
m_parent->cancel();
}
void interrupt() { m_interrupted = true; }
void setProgress(int percent) {
if(m_progress >= percent)
return;
m_progress = percent;
m_parent->scheduleUpdate();
}
private:
int m_progress; // always in percent
int m_weight;
bool m_interrupted;
KoProgressUpdater *m_parent;
};
class KoProgressUpdater::Private {
public:
Private(QProgressBar *p)
: progressBar(p),
totalWeight(0),
currentProgress(0),
action(0)
{
}
void update() {
// this method is called by the action. The action will ensure it is called
// serially from one thread only. With an updateUi followed directly after
// this one (forced to the Gui Thread).
lock.lock();
int totalProgress =0;
foreach(KoProgressUpdaterPrivate *updater, subtasks) {
if(updater->interrupted()) {
currentProgress = -1;
break;
}
int progress = updater->progress();
if(progress > 100 || progress < 0)
progress = updater->progress(); // see comment in KoProgressUpdaterPrivate cpp file
totalProgress += progress * updater->weight();
}
currentProgress = totalProgress / totalWeight;
lock.unlock();
}
void updateUi() {
if(currentProgress == -1) {
progressBar->setValue(progressBar->maximum());
// should we hide the progressbar after a little while?
return;
}
progressBar->setValue(currentProgress);
}
QProgressBar *progressBar;
QList<KoProgressUpdaterPrivate*> subtasks;
int totalWeight;
int currentProgress; // used for the update and updateUi methods. Don't use elsewhere
QMutex lock; // protects access to d->subtasks
KoAction *action;
};
KoProgressUpdater::KoProgressUpdater(QProgressBar *progressBar)
: d(new Private(progressBar))
{
Q_ASSERT(d->progressBar);
d->action = new KoAction(this);
d->action->setExecutePolicy(KoExecutePolicy::onlyLastPolicy);
connect(d->action, SIGNAL(triggered(const QVariant &)), SLOT(update()), Qt::DirectConnection);
connect(d->action, SIGNAL(updateUi(const QVariant &)), SLOT(updateUi()), Qt::DirectConnection);
}
KoProgressUpdater::~KoProgressUpdater() {
qDeleteAll(d->subtasks);
d->subtasks.clear();
}
void KoProgressUpdater::start(int range, const QString &text) {
d->lock.lock();
qDeleteAll(d->subtasks);
d->subtasks.clear();
d->progressBar->setRange(0, range-1);
d->progressBar->setValue(0);
if(! text.isEmpty())
d->progressBar->setFormat(text);
d->totalWeight = 0;
d->lock.unlock();
}
KoUpdater KoProgressUpdater::startSubtask(int weight) {
d->lock.lock();
KoProgressUpdaterPrivate *p = new KoProgressUpdaterPrivate(this, weight);
d->totalWeight += weight;
d->subtasks.append(p);
d->lock.unlock();
return KoUpdater(p);
}
void KoProgressUpdater::scheduleUpdate() {
d->action->execute();
}
void KoProgressUpdater::cancel() {
d->lock.lock();
foreach(KoProgressUpdaterPrivate *KoUpdater, d->subtasks) {
KoUpdater->setProgress(100);
KoUpdater->interrupt();
}
d->lock.unlock();
scheduleUpdate();
}
// -------- KoUpdater ----------
KoUpdater::KoUpdater(const KoUpdater &other) {
d = other.d;
}
KoUpdater::KoUpdater(KoProgressUpdaterPrivate *p)
{
d = p;
Q_ASSERT(p);
Q_ASSERT(!d.isNull());
}
void KoUpdater::cancel() {
if(!d.isNull())
d->cancel();
}
void KoUpdater::setProgress(int percent) {
if(!d.isNull())
d->setProgress(percent);
}
int KoUpdater::progress() const {
if(d.isNull())
return 100;
return d->progress();
}
bool KoUpdater::interrupted() const {
if(d.isNull())
return true;
return d->interrupted();
}
#include <KoProgressUpdater.moc>
/* This file is part of the KDE project
* Copyright (C) 2006-2007 Thomas Zander <zander@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOPROGRESSUPDATER_H
#define KOPROGRESSUPDATER_H
#include "koguiutils_export.h"
#include <QList>
#include <QString>
#include <QMutex>
#include <QPointer>
class QProgressBar;
class KoUpdater;
class KoProgressUpdaterPrivate;
class KoAction;
/**
* Allow multiple subtasks to safely update and report progress.
* This class is able to update a progress bar with the total progress
* of a project that may be separated into different subtasks.
* Each subtask will use one KoUpdater which that subtask can then report
* progress to. Each KoUpdater.setProgress() call will automatically calculate
* the total progress over the whole tasks made and update the progress bar
* with the total progress so far.
*
* This class is created specifically with threading in mind so that subtasks
* can report their progress from their personal subthread and the progress bar
* will be updated correctly and not more often then repaints can return.
*
* Typical usage can be:
* @code
KoProgressUpdater *pu = new KoProgressUpdater(myProgressBar);
pu->start(100);
// create the subtasks
KoUpdater smooth = pu->startSubtask(5);
KoUpdater scale = pu->startSubtask(5);
KoUpdater cleanup = pu->startSubtask(1);
@endcode
* Doing an smooth.setProgress(50) will move the progress bar to 50% of the share
* of task 'smooth' which is 5 / 11 of the total and thus to 22.
*/
class KOGUIUTILS_EXPORT KoProgressUpdater : public QObject {
Q_OBJECT
public:
/**
* Constructor.
* @param progressBar the progress bar to update.
*/
KoProgressUpdater(QProgressBar *progressBar);
/// destructor
virtual ~KoProgressUpdater();
/**
* Start a new task.
* This will invalidate any previously created subtasks and set the range of
* the progressBar as well as the text in the progressbar.
* @param range the total range of progress bar makes.
* @param text The text to show in the progressBar.
* @see QProgressBar::setRange()
* @see QProgressBar::setFormat()
*/
void start(int range = 100, const QString &text = "%p%");
/**
* After calling start() you can create any number of Updaters, one for each subtask.
* @param weight use a weight to specify the weight this subtask has compared
* to the rest of the subtasks.
*/
KoUpdater startSubtask(int weight=1);
/**
* Cancelling the action will make each subtask be marked as 'interrupted' and
* set the total progress to 100%.
*/
void cancel();
protected:
friend class KoProgressUpdaterPrivate;
void scheduleUpdate();
private:
class Private;
Private * const d;
Q_PRIVATE_SLOT(d, void update())
Q_PRIVATE_SLOT(d, void updateUi())
};
/**
* An KoUpdater is a helper for keeping the progress of each subtask up to speed.
* This class is not thread safe, and it should only be used from one thread.
* The thread it is used in can be different from any other subtask or the
* KoProgressUpdater, though.
* @see KoProgressUpdater::startSubtask()
*/
class KOGUIUTILS_EXPORT KoUpdater {
public:
/// copy constructor.
KoUpdater(const KoUpdater &other);
/**
* Call this when this subtask wants to abort all the actions.
*/
void cancel();
/**
* Update your progress. Progress is always from 0 to 100.
* The global progress shown to the user is determined by the total
* amount of subtasks there are. This means that each subtasks can just
* report its own private progress in the range from 0 to 100.
*/
void setProgress(int percent);
/**
* return true when this task should stop processing immediately.
* When the task has been cancelled all the subtasks will get interrupted
* and should stop working. It is therefor important to have repeated calls to
* this method at regular (time) intervals and as soon as the method returns true
* cancel the subtask.
* @return true when this task should stop processing immediately.
*/
bool interrupted() const;
/**
* return the progress this subtask has made.
*/
int progress() const;
protected:
friend class KoProgressUpdater;
KoUpdater(KoProgressUpdaterPrivate *p);
private:
QPointer<KoProgressUpdaterPrivate> d;
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment