1 /* This file is part of the KDE project
2  * Copyright (C) 2006, 2008 Thomas Zander <zander@kde.org>
3  * Copyright (C) 2007-2010 Boudewijn Rempt <boud@valdyas.org>
4  * Copyright (C) 2007-2008 C. Boemann <cbo@boemann.dk>
5  * Copyright (C) 2006-2007 Jan Hambrecht <jaham@gmx.net>
6  * Copyright (C) 2009 Thorsten Zachmann <zachmann@kde.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23 
24 #ifndef KOCANVASCONTROLLER_H
25 #define KOCANVASCONTROLLER_H
26 
27 #include "kritaflake_export.h"
28 #include <QObject>
29 
30 #include <QSize>
31 #include <QPoint>
32 #include <QPointF>
33 #include <QPointer>
34 
35 class KActionCollection;
36 class QRect;
37 class QRectF;
38 
39 
40 class KoShape;
41 class KoCanvasBase;
42 class KoCanvasControllerProxyObject;
43 
44 /**
45  * KoCanvasController is the base class for wrappers around your canvas
46  * that provides scrolling and zooming for your canvas.
47  *
48  * Flake does not provide a canvas, the application will have to
49  * implement a canvas themselves. You canvas can be QWidget-based
50  * or something we haven't invented yet -- as long the class that holds the canvas
51  * implements KoCanvasController, tools, scrolling and zooming will work.
52  *
53  * A KoCanvasController implementation acts as a decorator around the canvas widget
54  * and provides a way to scroll the canvas, allows the canvas to be centered
55  * in the viewArea and manages tool activation.
56  *
57  * <p>The using application can instantiate this class and add its
58  * canvas using the setCanvas() call. Which is designed so it can be
59  * called multiple times if you need to exchange one canvas
60  * widget for another, for instance, switching between a plain QWidget or a QOpenGLWidget.
61  *
62  * <p>There is _one_ KoCanvasController per canvas in your
63  * application.
64  *
65  * <p>The canvas widget is at most as big as the viewport of the scroll
66  * area, and when the view on the document is near its edges, smaller.
67  * In your canvas widget code, you can find the right place in your
68  * document in view coordinates (pixels) by adding the documentOffset
69  */
70 class KRITAFLAKE_EXPORT KoCanvasController
71 {
72 public:
73 
74     // proxy QObject: use this to connect to slots and signals.
75     QPointer<KoCanvasControllerProxyObject> proxyObject;
76 
77     /**
78      * Constructor.
79      * @param actionCollection the action collection for this canvas
80      */
81     explicit KoCanvasController(KActionCollection* actionCollection);
82     virtual ~KoCanvasController();
83 
84 public:
85     /**
86      * Returns the current margin that is used to pad the canvas with.
87      * This value is read from the KConfig property "canvasmargin"
88      */
89     virtual int margin() const;
90 
91     /**
92      * Set the new margin to pad the canvas with.
93      */
94     virtual void setMargin(int margin);
95 
96     /**
97      * compatibility with QAbstractScrollArea
98      */
99     virtual void scrollContentsBy(int dx, int dy) = 0;
100 
101     /**
102      * @return the size of the viewport
103      */
104     virtual QSizeF viewportSize() const = 0;
105 
106     /**
107      * Set the new canvas to be shown as a child
108      * Calling this will emit canvasRemoved() if there was a canvas before, and will emit
109      * canvasSet() with the new canvas.
110      * @param canvas the new canvas. The KoCanvasBase::canvas() will be called to retrieve the
111      *        actual widget which will then be added as child of this one.
112      */
113     virtual void setCanvas(KoCanvasBase *canvas) = 0;
114 
115     /**
116      * Return the currently set canvas. The default implementation will return Null
117      * @return the currently set canvas
118      */
119     virtual KoCanvasBase *canvas() const;
120 
121     /**
122      * return the amount of pixels vertically visible of the child canvas.
123      * @return the amount of pixels vertically visible of the child canvas.
124      */
125     virtual int visibleHeight() const = 0;
126 
127     /**
128      * return the amount of pixels horizontally visible of the child canvas.
129      * @return the amount of pixels horizontally visible of the child canvas.
130      */
131     virtual int visibleWidth() const = 0;
132 
133     /**
134      * return the amount of pixels that are not visible on the left side of the canvas.
135      * The leftmost pixel that is shown is returned.
136      */
137     virtual int canvasOffsetX() const = 0;
138 
139     /**
140      * return the amount of pixels that are not visible on the top side of the canvas.
141      * The topmost pixel that is shown is returned.
142      */
143     virtual int canvasOffsetY() const = 0;
144 
145     /**
146      * @brief Scrolls the content of the canvas so that the given rect is visible.
147      *
148      * The rect is to be specified in view coordinates (pixels). The scrollbar positions
149      * are changed so that the centerpoint of the rectangle is centered if possible.
150      *
151      * @param rect the rectangle to make visible
152      * @param smooth if true the viewport translation will make be just enough to ensure visibility, no more.
153      * @see KoViewConverter::documentToView()
154      */
155     virtual void ensureVisible(const QRectF &rect, bool smooth = false) = 0;
156 
157     /**
158      * @brief Scrolls the content of the canvas so that the given shape is visible.
159      *
160      * This is just a wrapper function of the above function.
161      *
162      * @param shape the shape to make visible
163      */
164     virtual void ensureVisible(KoShape *shape) = 0;
165 
166     /**
167      * @brief zooms in around the center.
168      *
169      * The center must be specified in **widget** coordinates. The scrollbar positions
170      * are changed so that the center becomes center if possible.
171      *
172      * @param center the position to zoom in on
173      */
174     virtual void zoomIn(const QPoint &center) = 0;
175 
176     /**
177      * @brief zooms out around the center.
178      *
179      * The center must be specified in **widget** coordinates. The scrollbar positions
180      * are changed so that the center becomes center if possible.
181      *
182      * @param center the position to zoom out around
183      */
184     virtual void zoomOut(const QPoint &center) = 0;
185 
186     /**
187      * @brief zooms around the center.
188      *
189      * The center must be specified in **widget** coordinates. The scrollbar positions
190      * are changed so that the center becomes center if possible.
191      *
192      * @param center the position to zoom around
193      * @param zoom the zoom to apply
194      */
195     virtual void zoomBy(const QPoint &center, qreal zoom) = 0;
196 
197     /**
198      * @brief zoom so that rect is exactly visible (as close as possible)
199      *
200      * The rect must be specified in **widget** coordinates. The scrollbar positions
201      * are changed so that the center of the rect becomes center if possible.
202      *
203      * @param rect the rect in **widget** coordinates that should fit the view afterwards
204      */
205     virtual void zoomTo(const QRect &rect) = 0;
206 
207     /**
208      * @brief repositions the scrollbars so previous center is once again center
209      *
210      * The previous center is cached from when the user uses the scrollbars or zoomTo
211      * are called. zoomTo is mostly used when a zoom tool of sorts have marked an area
212      * to zoom in on
213      *
214      * The success of this method is limited by the size of thing. But we try our best.
215      */
216     virtual void recenterPreferred() = 0;
217 
218     /**
219      * Sets the preferred center point in view coordinates (pixels).
220      * @param viewPoint the new preferred center
221      */
222     virtual void setPreferredCenter(const QPointF &viewPoint) = 0;
223 
224     /// Returns the currently set preferred center point in view coordinates (pixels)
225     virtual QPointF preferredCenter() const = 0;
226 
227     /**
228      * Move the canvas over the x and y distance of the parameter distance
229      * @param distance the distance in view coordinates (pixels).  A positive distance means moving the canvas up/left.
230      */
231     virtual void pan(const QPoint &distance) = 0;
232 
233     /**
234      * Move the canvas up. This behaves the same as \sa pan() with a positive y coordinate.
235      */
236     virtual void panUp() = 0;
237 
238     /**
239      * Move the canvas down. This behaves the same as \sa pan() with a negative y coordinate.
240      */
241     virtual void panDown() = 0;
242 
243     /**
244      * Move the canvas to the left. This behaves the same as \sa pan() with a positive x coordinate.
245      */
246     virtual void panLeft() = 0;
247 
248     /**
249      * Move the canvas to the right. This behaves the same as \sa pan() with a negative x coordinate.
250      */
251     virtual void panRight() = 0;
252 
253     /**
254      * Get the position of the scrollbar
255      */
256     virtual QPoint scrollBarValue() const = 0;
257 
258     /**
259      * Set the position of the scrollbar
260      * @param value the new values of the scroll bars
261      */
262     virtual void setScrollBarValue(const QPoint &value) = 0;
263 
264     /**
265      * Update the range of scroll bars
266      */
267     virtual void resetScrollBars() = 0;
268 
269     /**
270      * Called when the size of your document in view coordinates (pixels) changes, for instance when zooming.
271      *
272      * @param newSize the new size, in view coordinates (pixels), of the document.
273      * @param recalculateCenter if true the offset in the document we center on after calling
274      *      recenterPreferred() will be recalculated for the new document size so the visual offset stays the same.
275      */
276     virtual void updateDocumentSize(const QSizeF &sz, bool recalculateCenter) = 0;
277 
278     /**
279      * Set mouse wheel to zoom behaviour
280      * @param zoom if true wheel will zoom instead of scroll, control modifier will scroll
281      */
282     virtual void setZoomWithWheel(bool zoom) = 0;
283 
284     /**
285      * Set scroll area to be bigger than actual document.
286      * It allows the user to move the corner of the document
287      * to e.g. the center of the screen
288      *
289      * @param factor the coefficient, defining how much we can scroll out,
290      *     measured in parts of the widget size. Null value means vast
291      *     scrolling is disabled.
292      */
293     virtual void setVastScrolling(qreal factor) = 0;
294 
295    /**
296      * Returns the action collection for the window
297      * @returns action collection for this window, can be 0
298      */
299     KActionCollection* actionCollection() const;
300 
301     QPoint documentOffset() const;
302 
303     /**
304      * @return the current position of the cursor fetched from QCursor::pos() and
305      *         converted into document coordinates
306      */
307     virtual QPointF currentCursorPosition() const = 0;
308 
309 protected:
310     void setDocumentSize(const QSizeF &sz);
311     QSizeF documentSize() const;
312 
313     void setPreferredCenterFractionX(qreal);
314     qreal preferredCenterFractionX() const;
315 
316     void setPreferredCenterFractionY(qreal);
317     qreal preferredCenterFractionY() const;
318 
319     void setDocumentOffset( QPoint &offset);
320 
321 
322 private:
323     class Private;
324     Private * const d;
325 };
326 
327 
328 /**
329  * Workaround class for the problem that Qt does not allow two QObject base classes.
330  * KoCanvasController can be implemented by for instance QWidgets, so it cannot be
331  * a QObject directly. The interface of this class should be considered public interface
332  * for KoCanvasController.
333  */
334 class KRITAFLAKE_EXPORT KoCanvasControllerProxyObject : public QObject
335 {
336     Q_OBJECT
337     Q_DISABLE_COPY(KoCanvasControllerProxyObject)
338 public:
339     explicit KoCanvasControllerProxyObject(KoCanvasController *canvasController, QObject *parent = 0);
340 
341 public:
342 
343     // Convenience methods to invoke the signals from subclasses
344 
emitCanvasRemoved(KoCanvasController * canvasController)345     void emitCanvasRemoved(KoCanvasController *canvasController) { emit canvasRemoved(canvasController); }
emitCanvasSet(KoCanvasController * canvasController)346     void emitCanvasSet(KoCanvasController *canvasController) { emit canvasSet(canvasController); }
emitCanvasOffsetXChanged(int offset)347     void emitCanvasOffsetXChanged(int offset) { emit canvasOffsetXChanged(offset); }
emitCanvasOffsetYChanged(int offset)348     void emitCanvasOffsetYChanged(int offset) { emit canvasOffsetYChanged(offset); }
emitCanvasMousePositionChanged(const QPoint & position)349     void emitCanvasMousePositionChanged(const QPoint &position) { emit canvasMousePositionChanged(position); }
emitDocumentMousePositionChanged(const QPointF & position)350     void emitDocumentMousePositionChanged(const QPointF &position) { emit documentMousePositionChanged(position); }
emitSizeChanged(const QSize & size)351     void emitSizeChanged(const QSize &size) { emit sizeChanged(size); }
emitMoveDocumentOffset(const QPoint & point)352     void emitMoveDocumentOffset(const QPoint &point) { emit moveDocumentOffset(point); }
emitZoomRelative(const qreal factor,const QPointF & stillPoint)353     void emitZoomRelative(const qreal factor, const QPointF &stillPoint) { emit zoomRelative(factor, stillPoint); }
354 
355     // Convenience method to retrieve the canvas controller for who needs to use QPointer
canvasController()356     KoCanvasController *canvasController() const { return m_canvasController; }
357 
358 Q_SIGNALS:
359     /**
360      * Emitted when a previously added canvas is about to be removed.
361      * @param canvasController this object
362      */
363     void canvasRemoved(KoCanvasController *canvasController);
364 
365     /**
366      * Emitted when a canvas is set on this widget
367      * @param canvasController this object
368      */
369     void canvasSet(KoCanvasController *canvasController);
370 
371     /**
372      * Emitted when canvasOffsetX() changes
373      * @param offset the new canvas offset
374      */
375     void canvasOffsetXChanged(int offset);
376 
377     /**
378      * Emitted when canvasOffsetY() changes
379      * @param offset the new canvas offset
380      */
381     void canvasOffsetYChanged(int offset);
382 
383     /**
384      * Emitted when the cursor is moved over the canvas widget.
385      * @param position the position in view coordinates (pixels).
386      */
387     void canvasMousePositionChanged(const QPoint &position);
388 
389     /**
390      * Emitted when the cursor is moved over the canvas widget.
391      * @param position the position in document coordinates.
392      *
393      * Use \ref canvasMousePositionChanged to get the position
394      * in view coordinates.
395      */
396     void documentMousePositionChanged(const QPointF &position);
397 
398     /**
399      * Emitted when the entire controller size changes
400      * @param size the size in widget pixels.
401      */
402     void sizeChanged(const QSize &size);
403 
404     /**
405      * Emitted whenever the document is scrolled.
406      *
407      * @param point the new top-left point from which the document should
408      * be drawn.
409      */
410     void moveDocumentOffset(const QPoint &point);
411 
412     /**
413      * Emitted when zoomRelativeToPoint have calculated a factor by which
414      * the zoom should change and the point which should stand still
415      * on screen.
416      * Someone needs to connect to this and take action
417      *
418      * @param factor by how much the zoom needs to change.
419      * @param stillPoint the point which will not change its position
420      *                   in widget during the zooming. It is measured in
421      *                   view coordinate system *before* zoom.
422      */
423     void zoomRelative(const qreal factor, const QPointF &stillPoint);
424 
425 public Q_SLOTS:
426     /**
427      * Call this slot whenever the size of your document in view coordinates (pixels)
428      * changes, for instance when zooming.
429      * @param newSize the new size, in view coordinates (pixels), of the document.
430      * @param recalculateCenter if true the offset in the document we center on after calling
431      *      recenterPreferred() will be recalculated for the new document size so the visual offset stays the same.
432      */
433     void updateDocumentSize(const QSize &newSize, bool recalculateCenter = true);
434 
435 private:
436     KoCanvasController *m_canvasController;
437 };
438 
439 class KRITAFLAKE_EXPORT  KoDummyCanvasController : public KoCanvasController {
440 
441 public:
442 
KoDummyCanvasController(KActionCollection * actionCollection)443     explicit KoDummyCanvasController(KActionCollection* actionCollection)
444         : KoCanvasController(actionCollection)
445     {}
446 
~KoDummyCanvasController()447     ~KoDummyCanvasController() override
448     {}
449 
450 
scrollContentsBy(int,int)451     void scrollContentsBy(int /*dx*/, int /*dy*/) override {}
viewportSize()452     QSizeF viewportSize() const override { return QSizeF(); }
setCanvas(KoCanvasBase * canvas)453     void setCanvas(KoCanvasBase *canvas) override {Q_UNUSED(canvas)}
canvas()454     KoCanvasBase *canvas() const override {return 0;}
visibleHeight()455     int visibleHeight() const override {return 0;}
visibleWidth()456     int visibleWidth() const override {return 0;}
canvasOffsetX()457     int canvasOffsetX() const override {return 0;}
canvasOffsetY()458     int canvasOffsetY() const override {return 0;}
459     void ensureVisible(const QRectF &/*rect*/, bool /*smooth */ = false) override {}
ensureVisible(KoShape * shape)460     void ensureVisible(KoShape *shape) override {Q_UNUSED(shape)}
zoomIn(const QPoint &)461     void zoomIn(const QPoint &/*center*/) override {}
zoomOut(const QPoint &)462     void zoomOut(const QPoint &/*center*/) override {}
zoomBy(const QPoint &,qreal)463     void zoomBy(const QPoint &/*center*/, qreal /*zoom*/) override {}
zoomTo(const QRect &)464     void zoomTo(const QRect &/*rect*/) override {}
recenterPreferred()465     void recenterPreferred() override {}
setPreferredCenter(const QPointF &)466     void setPreferredCenter(const QPointF &/*viewPoint*/) override {}
preferredCenter()467     QPointF preferredCenter() const override {return QPointF();}
pan(const QPoint &)468     void pan(const QPoint &/*distance*/) override {}
panUp()469     void panUp() override {}
panDown()470     void panDown() override {}
panLeft()471     void panLeft() override {}
panRight()472     void panRight() override {}
scrollBarValue()473     QPoint scrollBarValue() const override {return QPoint();}
setScrollBarValue(const QPoint &)474     void setScrollBarValue(const QPoint &/*value*/) override {}
resetScrollBars()475     void resetScrollBars() override {}
updateDocumentSize(const QSizeF &,bool)476     void updateDocumentSize(const QSizeF &/*sz*/, bool /*recalculateCenter*/) override {}
setZoomWithWheel(bool)477     void setZoomWithWheel(bool /*zoom*/) override {}
setVastScrolling(qreal)478     void setVastScrolling(qreal /*factor*/) override {}
currentCursorPosition()479     QPointF currentCursorPosition() const override { return QPointF(); }
480 };
481 
482 #endif
483