1 /*
2  *  Copyright (c) 2006,2007 Thorsten Zachmann <zachmann@kde.org>
3  *  Copyright (c) 2009,2010 Cyrille Berger <cberger@cberger.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation;
8  * either version 2, or (at your option) any later version of the License.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library; see the file COPYING.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "Canvas.h"
22 
23 #include <QMenu>
24 #include <QMouseEvent>
25 #include <QPainter>
26 
27 #include <kxmlguifactory.h>
28 #include <kundo2command.h>
29 
30 #include <KoCanvasController.h>
31 #include <KoSelection.h>
32 #include <KoShapeManager.h>
33 #include <KoShapeLayer.h>
34 #include <KoToolProxy.h>
35 #include <KoUnit.h>
36 
37 #include "RootSection.h"
38 #include "View.h"
39 #include "ViewManager.h"
40 #include "Section.h"
41 #include "Layout.h"
42 #include "SectionContainer.h"
43 
Canvas(View * view,RootSection * doc,Section * currentSection)44 Canvas::Canvas(View* view, RootSection* doc, Section* currentSection)
45     : QWidget(view)
46     , KoCanvasBase(currentSection ? currentSection->sectionContainer() : 0)
47     , m_origin(0, 0)
48     , m_view(view)
49     , m_doc(doc)
50 {
51     m_shapeManager = new KoShapeManager(this);
52     connect(m_shapeManager, SIGNAL(selectionChanged()), SLOT(updateOriginAndSize()));
53     m_toolProxy = new KoToolProxy(this);
54     setFocusPolicy(Qt::StrongFocus);
55     // this is much faster than painting it in the paintevent
56     QPalette pal = palette();
57     pal.setColor(QPalette::Base, Qt::white);
58     pal.setColor(QPalette::Text, Qt::black);
59     setPalette(pal);
60     setBackgroundRole(QPalette::Base);
61     setAutoFillBackground(true);
62     setAttribute(Qt::WA_InputMethodEnabled, true);
63 
64     if(currentSection) {
65 
66         QList<KoShape*> shapes;
67         shapes.push_back(currentSection->sectionContainer()->layer());
68         shapeManager()->setShapes(shapes, KoShapeManager::AddWithoutRepaint);
69 
70         KoShapeLayer* layer = currentSection->sectionContainer()->layer();
71         shapeManager()->selection()->setActiveLayer(layer);
72 
73         // Make sure the canvas is enabled
74         setEnabled(true);
75 
76         update();
77     } else {
78         setEnabled(false);
79     }
80 }
81 
~Canvas()82 Canvas::~Canvas()
83 {
84     delete m_toolProxy;
85     delete m_shapeManager;
86 }
87 
setDocumentOffset(const QPoint & offset)88 void Canvas::setDocumentOffset(const QPoint &offset)
89 {
90     m_originalOffset = offset;
91     updateOffset();
92 }
93 
addCommand(KUndo2Command * command)94 void Canvas::addCommand(KUndo2Command *command)
95 {
96     m_doc->addCommand(m_view->activeSection(), command);
97     updateOriginAndSize();
98 }
99 
shapeManager() const100 KoShapeManager * Canvas::shapeManager() const
101 {
102     return m_shapeManager;
103 }
104 
updateCanvas(const QRectF & rc)105 void Canvas::updateCanvas(const QRectF& rc)
106 {
107     QRect clipRect(viewToWidget(viewConverter()->documentToView(rc).toRect()));
108     clipRect.adjust(-2, -2, 2, 2);   // Resize to fit anti-aliasing
109     clipRect.moveTopLeft(clipRect.topLeft() - m_documentOffset);
110     update(clipRect);
111 
112     emit canvasUpdated();
113 }
114 
viewConverter() const115 KoViewConverter * Canvas::viewConverter() const
116 {
117     return m_view->zoomHandler();
118 }
119 
unit() const120 KoUnit Canvas::unit() const
121 {
122     return KoUnit(KoUnit::Centimeter);
123 }
124 
documentOffset() const125 const QPoint & Canvas::documentOffset() const
126 {
127     return m_documentOffset;
128 }
129 
documentOrigin() const130 QPoint Canvas::documentOrigin() const
131 {
132     return m_origin;
133 }
134 
paintEvent(QPaintEvent * event)135 void Canvas::paintEvent(QPaintEvent *event)
136 {
137     QPainter painter(this);
138 
139     painter.translate(-documentOffset());
140     painter.setRenderHint(QPainter::Antialiasing);
141     QRectF clipRect = event->rect().translated(documentOffset());
142     painter.setClipRect(clipRect);
143 
144     painter.translate(m_origin.x(), m_origin.y());
145 
146     const KoViewConverter* converter = viewConverter();
147     shapeManager()->paint(painter, *converter, false);
148     painter.setRenderHint(QPainter::Antialiasing, false);
149 
150     painter.setRenderHint(QPainter::Antialiasing);
151     m_toolProxy->paint(painter, *converter);
152 }
153 
tabletEvent(QTabletEvent * event)154 void Canvas::tabletEvent(QTabletEvent *event)
155 {
156     m_toolProxy->tabletEvent(event, viewConverter()->viewToDocument(widgetToView(event->pos() + m_documentOffset)));
157 }
158 
mousePressEvent(QMouseEvent * event)159 void Canvas::mousePressEvent(QMouseEvent *event)
160 {
161     m_toolProxy->mousePressEvent(event, viewConverter()->viewToDocument(widgetToView(event->pos() + m_documentOffset)));
162 
163     if(!event->isAccepted() && event->button() == Qt::RightButton) {
164         showContextMenu(event->globalPos(), toolProxy()->popupActionList());
165     }
166 
167     event->setAccepted(true);
168 }
169 
mouseDoubleClickEvent(QMouseEvent * event)170 void Canvas::mouseDoubleClickEvent(QMouseEvent *event)
171 {
172     m_toolProxy->mouseDoubleClickEvent(event, viewConverter()->viewToDocument(widgetToView(event->pos() + m_documentOffset)));
173 }
174 
mouseMoveEvent(QMouseEvent * event)175 void Canvas::mouseMoveEvent(QMouseEvent *event)
176 {
177     m_toolProxy->mouseMoveEvent(event, viewConverter()->viewToDocument(widgetToView(event->pos() + m_documentOffset)));
178 }
179 
mouseReleaseEvent(QMouseEvent * event)180 void Canvas::mouseReleaseEvent(QMouseEvent *event)
181 {
182     m_toolProxy->mouseReleaseEvent(event, viewConverter()->viewToDocument(widgetToView(event->pos() + m_documentOffset)));
183 }
184 
keyPressEvent(QKeyEvent * event)185 void Canvas::keyPressEvent(QKeyEvent *event)
186 {
187     m_toolProxy->keyPressEvent(event);
188 #if 0
189 
190     if(! event->isAccepted()) {
191         event->accept();
192         switch(event->key()) {
193         case Qt::Key_Home:
194             m_view->navigatePage(KoPageApp::PageFirst);
195             break;
196         case Qt::Key_PageUp:
197             m_view->navigatePage(KoPageApp::PagePrevious);
198             break;
199         case Qt::Key_PageDown:
200             m_view->navigatePage(KoPageApp::PageNext);
201             break;
202         case Qt::Key_End:
203             m_view->navigatePage(KoPageApp::PageLast);
204             break;
205         default:
206             event->ignore();
207             break;
208         }
209     }
210     if(! event->isAccepted()) {
211         if(event->key() == Qt::Key_Backtab
212                 or(event->key() == Qt::Key_Tab && (event->modifiers() & Qt::ShiftModifier))) {
213             focusNextPrevChild(false);
214         } else if(event->key() == Qt::Key_Tab) {
215             focusNextPrevChild(true);
216         }
217     }
218 #endif
219 }
220 
keyReleaseEvent(QKeyEvent * event)221 void Canvas::keyReleaseEvent(QKeyEvent *event)
222 {
223     m_toolProxy->keyReleaseEvent(event);
224 }
225 
wheelEvent(QWheelEvent * event)226 void Canvas::wheelEvent(QWheelEvent * event)
227 {
228     m_toolProxy->wheelEvent(event, viewConverter()->viewToDocument(widgetToView(event->pos() + m_documentOffset)));
229 }
230 
closeEvent(QCloseEvent * event)231 void Canvas::closeEvent(QCloseEvent * event)
232 {
233     event->ignore();
234 }
235 
updateInputMethodInfo()236 void Canvas::updateInputMethodInfo()
237 {
238     updateMicroFocus();
239 }
240 
inputMethodQuery(Qt::InputMethodQuery query) const241 QVariant Canvas::inputMethodQuery(Qt::InputMethodQuery query) const
242 {
243     if (query == Qt::ImMicroFocus) {
244         QRectF rect = (m_toolProxy->inputMethodQuery(query, *(viewConverter())).toRectF()).toRect();
245         QPointF scroll(canvasController()->scrollBarValue());
246         rect.translate(documentOrigin() - scroll);
247         return rect.toRect();
248     }
249     return m_toolProxy->inputMethodQuery(query, *(viewConverter()));
250 }
251 
inputMethodEvent(QInputMethodEvent * event)252 void Canvas::inputMethodEvent(QInputMethodEvent *event)
253 {
254     m_toolProxy->inputMethodEvent(event);
255 }
256 
resizeEvent(QResizeEvent * event)257 void Canvas::resizeEvent(QResizeEvent * event)
258 {
259     emit sizeChanged(event->size());
260     updateOriginAndSize();
261 }
262 
showContextMenu(const QPoint & globalPos,const QList<QAction * > & actionList)263 void Canvas::showContextMenu(const QPoint& globalPos, const QList<QAction*>& actionList)
264 {
265     m_view->unplugActionList("toolproxy_action_list");
266     m_view->plugActionList("toolproxy_action_list", actionList);
267     QMenu *menu = dynamic_cast<QMenu*>(m_view->factory()->container("default_canvas_popup", m_view));
268 
269     if(menu)
270         menu->exec(globalPos);
271 }
272 
setBackgroundColor(const QColor & color)273 void Canvas::setBackgroundColor(const QColor &color)
274 {
275     QPalette pal = palette();
276     pal.setColor(QPalette::Normal, backgroundRole(), color);
277     pal.setColor(QPalette::Inactive, backgroundRole(), color);
278     setPalette(pal);
279 }
280 
updateOriginAndSize()281 void Canvas::updateOriginAndSize()
282 {
283     if(m_view->activeSection()) {
284         QRectF rect = m_view->activeSection()->layout()->boundingBox();
285         if(rect != m_oldDocumentRect) {
286             m_oldDocumentRect = rect;
287             emit(documentRect(rect));
288             update();
289         }
290         QRect viewRect = viewConverter()->documentToView(rect).toRect();
291         if(m_oldViewDocumentRect != viewRect) {
292             m_oldViewDocumentRect = viewRect;
293             m_origin = -viewRect.topLeft();
294             KoCanvasController* controller = canvasController();
295             if(controller) {
296                 // tell canvas controller the new document size in pixel
297                 controller->updateDocumentSize(viewRect.size(), true);
298                 // make sure the actual selection is visible
299                 KoSelection * selection = m_shapeManager->selection();
300                 if(selection->count())
301                     controller->ensureVisible(viewConverter()->documentToView(selection->boundingRect()));
302                 updateOffset();
303             }
304         }
305     }
306 }
307 
updateOffset()308 void Canvas::updateOffset()
309 {
310     qreal dx = qMax(0, (size().width() - m_oldViewDocumentRect.size().width()) / 2);
311     qreal dy = qMax(0, (size().height() - m_oldViewDocumentRect.size().height()) / 2);
312     m_documentOffset = m_originalOffset - QPoint(dx, dy);
313 }
314 
gridSize(qreal * horizontal,qreal * vertical) const315 void Canvas::gridSize(qreal *horizontal, qreal *vertical) const
316 {
317     *horizontal = 1;
318     *vertical = 1;
319 }
320 
snapToGrid() const321 bool Canvas::snapToGrid() const
322 {
323     return false;
324 }
325 
setCursor(const QCursor & cursor)326 void Canvas::setCursor(const QCursor &cursor)
327 {
328     QWidget::setCursor(cursor);
329 }
330 
focusInEvent(QFocusEvent * event)331 void Canvas::focusInEvent(QFocusEvent * event)
332 {
333     QWidget::focusInEvent(event);
334     emit(canvasReceivedFocus());
335 }
336 
widgetToView(const QPoint & p) const337 QPoint Canvas::widgetToView(const QPoint& p) const
338 {
339     return p - m_origin;
340 }
341 
widgetToView(const QRect & r) const342 QRect Canvas::widgetToView(const QRect& r) const
343 {
344     return r.translated(- m_origin);
345 }
346 
viewToWidget(const QPoint & p) const347 QPoint Canvas::viewToWidget(const QPoint& p) const
348 {
349     return p + m_origin;
350 }
351 
viewToWidget(const QRect & r) const352 QRect Canvas::viewToWidget(const QRect& r) const
353 {
354     return r.translated(m_origin);
355 }
356