chalk

kobirdeyepanel.cpp
1/*
2 * This file is part of the KDE project
3 *
4 * Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20#include <tqpixmap.h>
21#include <tqimage.h>
22#include <tqlayout.h>
23#include <tqpainter.h>
24#include <tqframe.h>
25#include <tqlabel.h>
26#include <tqtoolbutton.h>
27#include <tqslider.h>
28#include <tqcursor.h>
29
30#include <kdebug.h>
31#include <tdeglobalsettings.h>
32#include <tdeaction.h>
33#include <tdetoolbar.h>
34#include <knuminput.h>
35#include <tdelocale.h>
36
37#include <KoDocument.h>
38
39#include "wdgbirdeye.h"
40#include "kobirdeyepanel.h"
41#include "kis_int_spinbox.h"
42
43KoCanvasAdapter::KoCanvasAdapter() {}
44KoCanvasAdapter::~KoCanvasAdapter() {}
45
46KoZoomAdapter::KoZoomAdapter() {}
47KoZoomAdapter::~KoZoomAdapter() {}
48
49KoThumbnailAdapter::KoThumbnailAdapter() {}
50KoThumbnailAdapter::~KoThumbnailAdapter() {}
51
53 KoThumbnailAdapter * thumbnailProvider,
54 KoCanvasAdapter * canvas,
55 TQWidget * parent,
56 const char * name,
57 WFlags f)
58 : TQWidget(parent, name, f)
59 , m_zoomListener(zoomListener)
60 , m_thumbnailProvider(thumbnailProvider)
61 , m_canvas(canvas)
62 , m_dragging(false)
63{
64 TQHBoxLayout * l = new TQHBoxLayout(this);
65 m_page = new WdgBirdEye(this);
66 m_page->zoom->setRange((int) (TQMAX(1, 100 * zoomListener->getMinZoom())), (int) (100 * zoomListener->getMaxZoom()));
67 m_page->zoom->setValue(100);
68 m_page->zoom->setSuffix("%");
69
70 m_page->toolbar->setIconSize(16);
71 m_page->view->installEventFilter(this);
72 m_page->view->setBackgroundMode(TQt::NoBackground);
73
74 m_zoomIn = new TDEAction( i18n("Zoom In"), "birdeye_zoom_plus", 0, this, TQ_SLOT(zoomPlus()), this, "zoomIn" );
75 m_zoomOut = new TDEAction( i18n("Zoom Out"), "birdeye_zoom_minus", 0, this, TQ_SLOT(zoomMinus()), this, "zoomOut" );
76
77 l->addWidget(m_page);
78
79 connect(m_page->zoom, TQ_SIGNAL(valueChanged(int)), TQ_SLOT(zoomValueChanged(int)));
80 connect(m_page->bn100, TQ_SIGNAL(clicked()), TQ_SLOT(zoom100()));
81 connect(m_page->slZoom, TQ_SIGNAL(valueChanged(int)), TQ_SLOT(sliderChanged( int )));
82}
83
84KoBirdEyePanel::~KoBirdEyePanel()
85{
86 delete m_canvas;
87 delete m_thumbnailProvider;
88 delete m_zoomListener;
89}
90
91void KoBirdEyePanel::setZoom(int zoom)
92{
93 m_page->zoom->blockSignals(true);
94 m_page->slZoom->blockSignals(true);
95
96 m_page->zoom->setValue(zoom);
97
98 if (zoom < 10) {
99 m_page->slZoom->setValue(0);
100 }
101 else if (zoom > 10 && zoom < 100) {
102 m_page->slZoom->setValue(zoom / 10);
103 }
104 else if (zoom >= 100 && zoom < 150) {
105 m_page->slZoom->setValue(10);
106 }
107 else if (zoom >= 150 && zoom < 250) {
108 m_page->slZoom->setValue(11);
109 }
110 else if (zoom >= 250 && zoom < 350) {
111 m_page->slZoom->setValue(12);
112 }
113 else if (zoom >= 350 && zoom < 450) {
114 m_page->slZoom->setValue(13);
115 }
116 else if (zoom >= 450 && zoom < 550) {
117 m_page->slZoom->setValue(14);
118 }
119 else if (zoom >= 550 && zoom < 650) {
120 m_page->slZoom->setValue(15);
121 }
122 else if (zoom >= 650 && zoom < 875) {
123 m_page->slZoom->setValue(16);
124 }
125 else if (zoom >= 875 && zoom < 1150) {
126 m_page->slZoom->setValue(17);
127 }
128 else if (zoom >= 1150 && zoom < 1450) {
129 m_page->slZoom->setValue(18);
130 }
131 else if (zoom >= 1450) {
132 m_page->slZoom->setValue(19);
133 }
134
135
136 m_page->zoom->blockSignals(false);
137 m_page->slZoom->blockSignals(false);
138
139
140}
141
142void KoBirdEyePanel::zoomValueChanged(int zoom)
143{
144 KoPoint center;
145 center = m_canvas->visibleArea().center();
146 m_zoomListener->zoomTo(center.x(), center.y(), zoom / 100.0);
147 setZoom(zoom);
148}
149
150void KoBirdEyePanel::zoom100()
151{
152 zoomValueChanged( 100 );
153}
154
155void KoBirdEyePanel::sliderChanged( int v )
156{
157 if (v < 10) {
158 zoomValueChanged((v + 1) * 10);
159 }
160 else {
161 switch(v) {
162 case 10:
163 zoomValueChanged(100);
164 break;
165 case 11:
166 zoomValueChanged(200);
167 break;
168 case 12:
169 zoomValueChanged(300);
170 case 13:
171 zoomValueChanged(400);
172 break;
173 case 14:
174 zoomValueChanged(500);
175 break;
176 case 15:
177 zoomValueChanged(600);
178 break;
179 case 16:
180 zoomValueChanged(750);
181 break;
182 case 17:
183 zoomValueChanged(1000);
184 break;
185 case 18:
186 zoomValueChanged(1300);
187 break;
188 case 19:
189 zoomValueChanged(1600);
190 break;
191 }
192 }
193}
194
195void KoBirdEyePanel::cursorPosChanged(TQ_INT32 xpos, TQ_INT32 ypos)
196{
197 m_page->txtX->setText(TQString("%L1").arg(xpos, 5));
198 m_page->txtY->setText(TQString("%L1").arg(ypos, 5));
199}
200
201void KoBirdEyePanel::setThumbnailProvider(KoThumbnailAdapter * thumbnailProvider)
202{
203 delete m_thumbnailProvider;
204 m_thumbnailProvider = thumbnailProvider;
205}
206
208{
209 updateVisibleArea();
210 renderView();
211 m_page->view->update();
212 setZoom(tqRound(m_canvas->zoomFactor() * 100));
213}
214
215void KoBirdEyePanel::slotUpdate(const TQRect & r)
216{
217 TQRect updateRect = r;
218
219 if (m_thumbnailProvider->pixelSize() != m_documentSize) {
220 m_documentSize = m_thumbnailProvider->pixelSize();
221 fitThumbnailToView();
222 updateRect = TQRect(0, 0, m_documentSize.width(), m_documentSize.height());
223 }
224
225 updateRect &= TQRect(0, 0, m_documentSize.width(), m_documentSize.height());
226
227 if (!updateRect.isEmpty() && !m_documentSize.isEmpty()) {
228
229 TQRect thumbnailRect = documentToThumbnail(KoRect::fromTQRect(updateRect));
230
231 if (!thumbnailRect.isEmpty()) {
232
233 TQImage thumbnailImage = m_thumbnailProvider->image(thumbnailRect, m_thumbnail.size());
234
235 if (!thumbnailImage.isNull()) {
236
237 Q_ASSERT(thumbnailImage.size() == thumbnailRect.size());
238
239 TQPainter painter(&m_thumbnail);
240
241 painter.fillRect(thumbnailRect, colorGroup().mid());
242 painter.drawImage(thumbnailRect.x(), thumbnailRect.y(), thumbnailImage);
243 }
244 }
245 }
246
247 renderView();
248 m_page->view->update();
249}
250
251TQRect KoBirdEyePanel::documentToThumbnail(const KoRect& docRect)
252{
253 if (docRect.isEmpty() || m_documentSize.isEmpty() || m_thumbnail.isNull()) {
254 return TQRect();
255 }
256
257 TQ_INT32 thumbnailLeft = static_cast<TQ_INT32>((docRect.left() * m_thumbnail.width()) / m_documentSize.width());
258 TQ_INT32 thumbnailRight = static_cast<TQ_INT32>(((docRect.right() + 1) * m_thumbnail.width()) / m_documentSize.width());
259 TQ_INT32 thumbnailTop = static_cast<TQ_INT32>((docRect.top() * m_thumbnail.height()) / m_documentSize.height());
260 TQ_INT32 thumbnailBottom = static_cast<TQ_INT32>(((docRect.bottom() + 1) * m_thumbnail.height()) / m_documentSize.height());
261
262 TQRect thumbnailRect(thumbnailLeft, thumbnailTop, thumbnailRight - thumbnailLeft + 1, thumbnailBottom - thumbnailTop + 1);
263 thumbnailRect &= m_thumbnail.rect();
264
265 return thumbnailRect;
266}
267
268KoRect KoBirdEyePanel::thumbnailToDocument(const TQRect& thumbnailRect)
269{
270 if (thumbnailRect.isEmpty() || m_documentSize.isEmpty() || m_thumbnail.isNull()) {
271 return KoRect();
272 }
273
274 double docLeft = (static_cast<double>(thumbnailRect.left()) * m_documentSize.width()) / m_thumbnail.width();
275 double docRight = (static_cast<double>(thumbnailRect.right() + 1) * m_documentSize.width()) / m_thumbnail.width();
276 double docTop = (static_cast<double>(thumbnailRect.top()) * m_documentSize.height()) / m_thumbnail.height();
277 double docBottom = (static_cast<double>(thumbnailRect.bottom() + 1) * m_documentSize.height()) / m_thumbnail.height();
278
279 KoRect docRect(docLeft, docTop, docRight - docLeft + 1, docBottom - docTop + 1);
280 docRect &= KoRect(0, 0, m_documentSize.width(), m_documentSize.height());
281
282 return docRect;
283}
284
285TQPoint KoBirdEyePanel::viewToThumbnail(const TQPoint& viewPoint)
286{
287 int thumbnailX = (m_viewBuffer.width() - m_thumbnail.width()) / 2;
288 int thumbnailY = (m_viewBuffer.height() - m_thumbnail.height()) / 2;
289
290 return TQPoint(viewPoint.x() - thumbnailX, viewPoint.y() - thumbnailY);
291}
292
293
294void KoBirdEyePanel::zoomMinus()
295{
296}
297
298void KoBirdEyePanel::zoomPlus()
299{
300}
301
302void KoBirdEyePanel::updateVisibleArea()
303{
304 m_visibleAreaInThumbnail = documentToThumbnail(m_canvas->visibleArea());
305}
306
307
308bool KoBirdEyePanel::eventFilter(TQObject* o, TQEvent* ev)
309{
310 if (o == m_page->view && ev->type() == TQEvent::Resize) {
311 resizeViewEvent(static_cast<TQResizeEvent*>(ev)->size());
312 }
313
314 if (o == m_page->view && ev->type() == TQEvent::Paint) {
315 paintViewEvent(static_cast<TQPaintEvent*>(ev));
316 }
317
318 if (o == m_page->view && ev->type() == TQEvent::MouseMove) {
319
320 TQMouseEvent* me = (TQMouseEvent*)ev;
321 TQPoint thumbnailPos = viewToThumbnail(me->pos());
322
323 if (m_dragging) {
324 handleMouseMoveAction(thumbnailPos);
325 } else {
326 handleMouseMove(thumbnailPos);
327 }
328
329 return true;
330 }
331
332 if (o == m_page->view && ev->type() == TQEvent::MouseButtonPress) {
333
334 TQMouseEvent* me = (TQMouseEvent*)ev;
335 TQPoint thumbnailPos = viewToThumbnail(me->pos());
336
337 if (me->button() == TQt::LeftButton) {
338 handleMousePress(thumbnailPos);
339 }
340
341 return true;
342 }
343
344 if (o == m_page->view && ev->type() == TQEvent::MouseButtonRelease) {
345
346 TQMouseEvent* me = (TQMouseEvent*)ev;
347
348 if (me->button() == TQt::LeftButton) {
349 m_dragging = false;
350 }
351
352 return true;
353 }
354
355 return m_page->eventFilter(o, ev);
356}
357
358KoBirdEyePanel::enumDragHandle KoBirdEyePanel::dragHandleAt(TQPoint p)
359{
360 TQRect left = TQRect(m_visibleAreaInThumbnail.left()-1, m_visibleAreaInThumbnail.top()-1, 3, m_visibleAreaInThumbnail.height()+2);
361 TQRect right = TQRect(m_visibleAreaInThumbnail.right()-1, m_visibleAreaInThumbnail.top()-1, 3, m_visibleAreaInThumbnail.height()+2);
362 TQRect top = TQRect(m_visibleAreaInThumbnail.left()-1, m_visibleAreaInThumbnail.top()-1, m_visibleAreaInThumbnail.width()+2, 3);
363 TQRect bottom = TQRect(m_visibleAreaInThumbnail.left()-1, m_visibleAreaInThumbnail.bottom()-1, m_visibleAreaInThumbnail.width()+2, 3);
364
365 if (left.contains(p)) {
366 return DragHandleLeft;
367 }
368
369 if (right.contains(p)) {
370 return DragHandleRight;
371 }
372
373 if (top.contains(p)) {
374 return DragHandleTop;
375 }
376
377 if (bottom.contains(p)) {
378 return DragHandleBottom;
379 }
380
381 if (m_visibleAreaInThumbnail.contains(p)) {
382 return DragHandleCentre;
383 }
384
385 return DragHandleNone;
386}
387
388void KoBirdEyePanel::handleMouseMove(TQPoint p)
389{
390 TQCursor cursor;
391
392 switch (dragHandleAt(p)) {
393 case DragHandleLeft:
394 case DragHandleRight:
395 cursor = TQt::sizeHorCursor;
396 break;
397 case DragHandleTop:
398 case DragHandleBottom:
399 cursor = TQt::sizeVerCursor;
400 break;
401 case DragHandleCentre:
402 cursor = TQt::sizeAllCursor;
403 break;
404 default:
405 case DragHandleNone:
406 if (m_thumbnail.rect().contains(p)) {
407 cursor = TQt::PointingHandCursor;
408 } else {
409 cursor = TQt::arrowCursor;
410 }
411 break;
412 }
413
414 m_page->view->setCursor(cursor);
415}
416
417void KoBirdEyePanel::handleMouseMoveAction(TQPoint p)
418{
419 if (m_dragging) {
420
421 TQ_INT32 dx = p.x() - m_lastDragPos.x();
422 TQ_INT32 dy = p.y() - m_lastDragPos.y();
423
424 m_lastDragPos = p;
425
426 TQRect thumbnailRect = m_visibleAreaInThumbnail;
427
428 switch (m_dragHandle) {
429 case DragHandleLeft: {
430 thumbnailRect.setLeft(thumbnailRect.left()+dx);
431 break;
432 }
433 case DragHandleRight: {
434 thumbnailRect.setRight(thumbnailRect.right()+dx);
435 break;
436 }
437 case DragHandleTop: {
438 thumbnailRect.setTop(thumbnailRect.top()+dy);
439 break;
440 }
441 case DragHandleBottom: {
442 thumbnailRect.setBottom(thumbnailRect.bottom()+dy);
443 break;
444 }
445 case DragHandleCentre: {
446 thumbnailRect.moveBy(dx, dy);
447 break;
448 }
449 default:
450 case DragHandleNone:
451 break;
452 }
453
454 makeThumbnailRectVisible(thumbnailRect);
455 }
456}
457
458void KoBirdEyePanel::handleMousePress(TQPoint p)
459{
460 if (!m_dragging) {
461
462 enumDragHandle dragHandle = dragHandleAt(p);
463
464 if (dragHandle == DragHandleNone) {
465 if (m_thumbnail.rect().contains(p)) {
466
467 // Snap visible area centre to p and begin a centre drag.
468
469 TQRect thumbnailRect = m_visibleAreaInThumbnail;
470 thumbnailRect.moveCenter(p);
471 makeThumbnailRectVisible(thumbnailRect);
472
473 m_dragHandle = DragHandleCentre;
474 m_page->view->setCursor(TQt::sizeAllCursor);
475 m_dragging = true;
476 }
477 } else {
478 m_dragHandle = dragHandle;
479 m_dragging = true;
480 }
481 m_lastDragPos = p;
482 }
483}
484
485void KoBirdEyePanel::makeThumbnailRectVisible(const TQRect& r)
486{
487 if (r.isEmpty()) {
488 return;
489 }
490
491 TQRect thumbnailRect = r;
492
493 if (thumbnailRect.left() < m_thumbnail.rect().left()) {
494 thumbnailRect.moveLeft(m_thumbnail.rect().left());
495 }
496 if (thumbnailRect.right() > m_thumbnail.rect().right()) {
497 thumbnailRect.moveRight(m_thumbnail.rect().right());
498 }
499 if (thumbnailRect.top() < m_thumbnail.rect().top()) {
500 thumbnailRect.moveTop(m_thumbnail.rect().top());
501 }
502 if (thumbnailRect.bottom() > m_thumbnail.rect().bottom()) {
503 thumbnailRect.moveBottom(m_thumbnail.rect().bottom());
504 }
505
506 if (thumbnailRect.width() > m_thumbnail.rect().width()) {
507 thumbnailRect.setLeft(m_thumbnail.rect().left());
508 thumbnailRect.setRight(m_thumbnail.rect().right());
509 }
510 if (thumbnailRect.height() > m_thumbnail.rect().height()) {
511 thumbnailRect.setTop(m_thumbnail.rect().top());
512 thumbnailRect.setBottom(m_thumbnail.rect().bottom());
513 }
514
515 double zoomFactor = m_canvas->zoomFactor();
516
517 if (thumbnailRect.size() == m_visibleAreaInThumbnail.size()) {
518 // No change to zoom
519 } else if (thumbnailRect.width() != m_visibleAreaInThumbnail.width()) {
520
521 Q_ASSERT(thumbnailRect.height() == m_visibleAreaInThumbnail.height());
522
523 zoomFactor *= static_cast<double>(m_visibleAreaInThumbnail.width()) / thumbnailRect.width();
524 } else {
525
526 Q_ASSERT(thumbnailRect.width() == m_visibleAreaInThumbnail.width());
527
528 zoomFactor *= static_cast<double>(m_visibleAreaInThumbnail.height()) / thumbnailRect.height();
529 }
530
531 if (zoomFactor < m_zoomListener->getMinZoom()) {
532 zoomFactor = m_zoomListener->getMinZoom();
533 } else if (zoomFactor > m_zoomListener->getMaxZoom()) {
534 zoomFactor = m_zoomListener->getMaxZoom();
535 }
536
537 KoRect docRect = thumbnailToDocument(thumbnailRect);
538 m_zoomListener->zoomTo(docRect.center().x(), docRect.center().y(), zoomFactor);
539}
540
541void KoBirdEyePanel::resizeViewEvent(TQSize size)
542{
543 m_viewBuffer.resize(size);
544 fitThumbnailToView();
545 slotUpdate(TQRect(0, 0, m_documentSize.width(), m_documentSize.height()));
546}
547
548void KoBirdEyePanel::fitThumbnailToView()
549{
550 TQRect docRect = TQRect(0, 0, m_thumbnailProvider->pixelSize().width(), m_thumbnailProvider->pixelSize().height());
551 TQ_INT32 thumbnailWidth;
552 TQ_INT32 thumbnailHeight;
553
554 if (docRect.isEmpty()) {
555 thumbnailWidth = 0;
556 thumbnailHeight = 0;
557 } else {
558 const int thumbnailBorderPixels = 4;
559
560 double xScale = double(m_page->view->contentsRect().width() - thumbnailBorderPixels) / docRect.width();
561 double yScale = double(m_page->view->contentsRect().height() - thumbnailBorderPixels) / docRect.height();
562
563 if (xScale < yScale) {
564 thumbnailWidth = m_page->view->contentsRect().width() - thumbnailBorderPixels;
565 thumbnailHeight = TQ_INT32(ceil(docRect.height() * xScale));
566 } else {
567 thumbnailWidth = TQ_INT32(ceil(docRect.width() * yScale));
568 thumbnailHeight = m_page->view->contentsRect().height() - thumbnailBorderPixels;
569 }
570 }
571
572 m_thumbnail.resize(thumbnailWidth, thumbnailHeight);
573 updateVisibleArea();
574}
575
576void KoBirdEyePanel::renderView()
577{
578 Q_ASSERT(!m_viewBuffer.isNull());
579
580 if (!m_viewBuffer.isNull()) {
581
582 updateVisibleArea();
583
584 TQPainter painter(&m_viewBuffer);
585
586 painter.fillRect(0, 0, m_viewBuffer.width(), m_viewBuffer.height(), colorGroup().mid());
587
588 if (!m_thumbnail.isNull()) {
589
590 int thumbnailX = (m_viewBuffer.width() - m_thumbnail.width()) / 2;
591 int thumbnailY = (m_viewBuffer.height() - m_thumbnail.height()) / 2;
592
593 painter.drawPixmap(thumbnailX, thumbnailY, m_thumbnail);
594
595 painter.setPen(TQt::red);
596 painter.drawRect(thumbnailX + m_visibleAreaInThumbnail.x() - 1,
597 thumbnailY + m_visibleAreaInThumbnail.y() - 1,
598 m_visibleAreaInThumbnail.width() + 2,
599 m_visibleAreaInThumbnail.height() + 2);
600 painter.setPen(TQt::red.light());
601 painter.drawRect(thumbnailX + m_visibleAreaInThumbnail.x() - 2,
602 thumbnailY + m_visibleAreaInThumbnail.y() - 2,
603 m_visibleAreaInThumbnail.width() + 4,
604 m_visibleAreaInThumbnail.height() + 4);
605 }
606 }
607}
608
609void KoBirdEyePanel::paintViewEvent(TQPaintEvent *e)
610{
611 Q_ASSERT(!m_viewBuffer.isNull());
612
613 if (!m_viewBuffer.isNull()) {
614 bitBlt(m_page->view, e->rect().x(), e->rect().y(), &m_viewBuffer,
615 e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height());
616 }
617}
618
619#include "kobirdeyepanel.moc"
void slotViewTransformationChanged()
Connect to this slot to inform the bird's eye view of changes in the view transformation,...
KoBirdEyePanel(KoZoomAdapter *zoomListener, KoThumbnailAdapter *thumbnailProvider, KoCanvasAdapter *canvas, TQWidget *parent, const char *name=0, WFlags f=0)
Create a new bird eye panel.
KoRect thumbnailToDocument(const TQRect &thumbnailRect)
Returns the rectangle in the document covered by the given thumbnail rectangle.
void setThumbnailProvider(KoThumbnailAdapter *thumbnailProvider)
Set a new thumbnail provider.
void slotUpdate(const TQRect &r)
Connect to this slot if a (rectangular) area of your document is changed.
TQRect documentToThumbnail(const KoRect &docRect)
Returns the rectangle in the thumbnail covered by the given document rectangle.
TQPoint viewToThumbnail(const TQPoint &viewPoint)
Converts a point in the view to a point in the thumbnail.
The zoom listener interface defines methods that the bird eye panel will call whenever the zoomlevel ...
virtual double getMaxZoom()=0
Get the maximum zoom factor that this listener supports.
virtual double getMinZoom()=0
Get the minimum zoom factor that this listener supports.