1 /***************************************************************************
2 kmagview.cpp - description
3 -------------------
4 begin : Mon Feb 12 23:45:41 EST 2001
5 copyright : (C) 2001-2003 by Sarang Lakare
6 email : sarang#users.sourceforge.net
7 copyright : (C) 2003-2004 by Olaf Schmidt
8 email : ojschmidt@kde.org
9 copyright : (C) 2008 by Matthew Woehlke
10 email : mw_triad@users.sourceforge.net
11 copyright (C) 2010 Sebastian Sauer
12 email sebsauer@kdab.com
13 ***************************************************************************/
14
15 /***************************************************************************
16 * *
17 * This program is free software; you can redistribute it and/or modify *
18 * it under the terms of the GNU General Public License as published by *
19 * the Free Software Foundation; either version 2 of the License, or *
20 * (at your option) any later version. *
21 * *
22 ***************************************************************************/
23
24
25 // application specific includes
26 #include "kmagzoomview.h"
27 #include "colorsim.h"
28
29 // include files for Qt
30 #include <QApplication>
31 #include <QBitmap>
32 #include <QDesktopWidget>
33 #include <QScrollBar>
34 #include <QScreen>
35 #include <QPainter>
36
37 // include files for KF5
38 #include <KLocalizedString>
39 #include <chrono>
40
41 using namespace std::chrono_literals;
42
43 #ifdef QAccessibilityClient_FOUND
44 #include <qaccessibilityclient/accessibleobject.h>
45 #endif
46
47 // include bitmaps for cursors
48 static const uchar left_ptr_bits[] = {
49 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00,
50 0xf8, 0x01, 0xf8, 0x03, 0xf8, 0x07, 0xf8, 0x00, 0xd8, 0x00, 0x88, 0x01,
51 0x80, 0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00};
52
53 static const uchar left_ptrmsk_bits[] = {
54 0x0c, 0x00, 0x1c, 0x00, 0x3c, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xfc, 0x01,
55 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x01, 0xdc, 0x03,
56 0xcc, 0x03, 0x80, 0x07, 0x80, 0x07, 0x00, 0x03};
57
58 static const uchar phand_bits[] = {
59 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
60 0x7e, 0x04, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x70, 0x08, 0x00, 0x00,
61 0x08, 0x08, 0x00, 0x00, 0x70, 0x14, 0x00, 0x00, 0x08, 0x22, 0x00, 0x00,
62 0x30, 0x41, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x40, 0x12, 0x00, 0x00,
63 0x80, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
70 static const uchar phandm_bits[] = {
71 0xfe, 0x01, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0xff, 0x07, 0x00, 0x00,
72 0xff, 0x0f, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00,
73 0xfc, 0x1f, 0x00, 0x00, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00,
74 0xf8, 0xff, 0x00, 0x00, 0xf0, 0x7f, 0x00, 0x00, 0xe0, 0x3f, 0x00, 0x00,
75 0xc0, 0x1f, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
76 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
82
83
KMagZoomView(QWidget * parent,const char * name)84 KMagZoomView::KMagZoomView(QWidget *parent, const char *name)
85 : QAbstractScrollArea(parent),
86 m_selRect(0, 0, 128, 128, this),
87 m_grabTimer(parent),
88 m_mouseViewTimer(parent),
89 m_latestCursorPos(0,0),
90 m_followMouse(false),
91 m_followFocus(false),
92 m_showMouse(1),
93 m_zoom(1.0),
94 m_rotation(0),
95 m_colormode(0),
96 m_fitToWindow(true)
97 {
98 setObjectName( QLatin1String( name ));
99
100 viewport()->setMouseTracking(true);
101 viewport()->setAttribute(Qt::WA_NoSystemBackground, true);
102 viewport()->setAutoFillBackground(false);
103 viewport()->setFocusPolicy(Qt::StrongFocus);
104
105 // init the zoom matrix
106 setupMatrix();
107
108 m_ctrlKeyPressed = false;
109 m_shiftKeyPressed = false;
110 m_refreshSwitch = true;
111 m_refreshSwitchStateOnHide = m_refreshSwitch;
112
113 // set the refresh rate
114 setRefreshRate(10);
115
116 // connect it to grabFrame()
117 connect(&m_grabTimer, &QTimer::timeout, this, &KMagZoomView::grabFrame);
118 // start the grabTimer
119 m_grabTimer.start(static_cast<int>(1000.0/m_fps));
120
121 // connect it to updateMouseView()
122 connect(&m_mouseViewTimer, &QTimer::timeout, this, &KMagZoomView::updateMouseView);
123 // start the grabTimer @ 25 frames per second!
124 m_mouseViewTimer.start(40ms);
125
126 this->setWhatsThis( i18n("This is the main window which shows the contents of the\
127 selected region. The contents will be magnified according to the zoom level that is set."));
128
129 // different ways to show the cursor.
130 m_showMouseTypes << QStringLiteral( "Hidden" ) << QStringLiteral( "Box" ) << QStringLiteral( "Arrow" ) << QStringLiteral( "Actual" );
131
132 if(m_fitToWindow)
133 fitToWindow();
134
135 #ifdef QAccessibilityClient_FOUND
136 //subscribe to focus events from registry
137 m_registry.subscribeEventListeners(QAccessibleClient::Registry::Focus | QAccessibleClient::Registry::TextCaretMoved);
138 #endif
139 }
140
~KMagZoomView()141 KMagZoomView::~KMagZoomView()
142 {
143 }
144
contentsX() const145 int KMagZoomView::contentsX() const
146 {
147 return horizontalScrollBar()->value();
148 }
149
contentsY() const150 int KMagZoomView::contentsY() const
151 {
152 return verticalScrollBar()->value();
153 }
154
contentsWidth() const155 int KMagZoomView::contentsWidth() const
156 {
157 return horizontalScrollBar()->pageStep();
158 }
159
contentsHeight() const160 int KMagZoomView::contentsHeight() const
161 {
162 return verticalScrollBar()->pageStep();
163 }
164
visibleWidth() const165 int KMagZoomView::visibleWidth() const
166 {
167 return viewport()->width();
168 }
169
visibleHeight() const170 int KMagZoomView::visibleHeight() const
171 {
172 return viewport()->height();
173 }
174
setContentsPos(int x,int y)175 void KMagZoomView::setContentsPos(int x, int y)
176 {
177 horizontalScrollBar()->setValue(x);
178 verticalScrollBar()->setValue(y);
179 }
180
setupMatrix()181 void KMagZoomView::setupMatrix()
182 {
183 m_zoomMatrix.reset();
184 m_zoomMatrix.scale(m_zoom, m_zoom);
185 m_zoomMatrix.rotate(m_rotation);
186 }
187
188 /**
189 * This function will set/reset mouse following of grab window.
190 */
followMouse(bool follow)191 void KMagZoomView::followMouse(bool follow)
192 {
193 m_followMouse = follow;
194 m_mouseMode = Normal;
195 if(follow) {
196 setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
197 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
198 } else {
199 setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
200 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
201 }
202 }
203
204 #ifdef QAccessibilityClient_FOUND
205
followBoth(bool follow)206 void KMagZoomView::followBoth(bool follow)
207 {
208 m_followBoth = follow;
209 if(follow){
210 m_followMouse = true;
211 m_followFocus = false;
212 setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
213 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
214
215 connect(&m_registry, SIGNAL(focusChanged(QAccessibleClient::AccessibleObject)),
216 this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
217 connect(&m_registry, SIGNAL(textCaretMoved(QAccessibleClient::AccessibleObject,int)),
218 this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
219 } else {
220 setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
221 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
222
223 disconnect(this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
224 }
225 }
226
227 /**
228 * This function will set/reset keyboard focus following of grab window.
229 */
followFocus(bool follow)230 void KMagZoomView::followFocus(bool follow)
231 {
232 if(m_followFocus == follow)
233 return;
234 m_followFocus = follow;
235 m_mouseMode = Normal;
236 if(follow) {
237 setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
238 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
239
240 connect(&m_registry,SIGNAL(focusChanged(QAccessibleClient::AccessibleObject)),
241 this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
242 connect(&m_registry, SIGNAL(textCaretMoved(QAccessibleClient::AccessibleObject,int)),
243 this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
244 } else {
245 setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
246 setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
247
248 disconnect(this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
249 }
250 }
251
focusChanged(const QAccessibleClient::AccessibleObject & object)252 void KMagZoomView::focusChanged(const QAccessibleClient::AccessibleObject &object)
253 {
254 m_oldFocus = object.focusPoint();
255 if(m_followBoth && !m_selRect.contains(m_oldFocus)) {
256 QCursor::setPos(m_oldFocus);
257 m_followFocus = true;
258 m_followMouse = false;
259 }
260 }
261
262 #endif
263
264 /**
265 * Called when the widget is hidden. Stop refresh when this happens.
266 */
hideEvent(QHideEvent *)267 void KMagZoomView::hideEvent( QHideEvent* )
268 {
269 // Save the state of the refresh switch.. the state will be restored
270 // when showEvent is called
271 m_refreshSwitchStateOnHide = m_refreshSwitch;
272
273 // Check if refresh is ON
274 if(m_refreshSwitch) {
275 toggleRefresh();
276 }
277 }
278
279
280 /**
281 * Called when the widget is shown. Start refresh when this happens.
282 */
showEvent(QShowEvent *)283 void KMagZoomView::showEvent( QShowEvent* )
284 {
285 // Check if refresh switch was ON when hide was called and if currently it is OFF
286 if(m_refreshSwitchStateOnHide && !m_refreshSwitch) {
287 // start the refresh in that case
288 toggleRefresh();
289 }
290 }
291
292 /**
293 * Called when the widget is resized. Check if fitToWindow is active when this happens.
294 */
resizeEvent(QResizeEvent * e)295 void KMagZoomView::resizeEvent( QResizeEvent * e )
296 {
297 horizontalScrollBar()->setRange(0, contentsWidth() - visibleWidth());
298 verticalScrollBar()->setRange(0, contentsHeight() - visibleHeight());
299 QAbstractScrollArea::resizeEvent(e);
300 if(m_fitToWindow)
301 fitToWindow();
302 }
303
304 /**
305 * Called when the widget is to be repainted.
306 *
307 * @param p
308 */
paintEvent(QPaintEvent * e)309 void KMagZoomView::paintEvent(QPaintEvent *e)
310 {
311 if(m_coloredPixmap.isNull())
312 return;
313
314 QPainter p(viewport());
315 int clipx = e->rect().x();
316 int clipy = e->rect().x();
317 int clipw = e->rect().width();
318 int cliph = e->rect().height();
319
320 // Paint empty areas Qt::black
321 if (contentsX()+contentsWidth() < visibleWidth())
322 p.fillRect (
323 QRect (contentsX()+contentsWidth(), clipy, visibleWidth()-contentsX()-contentsWidth(), cliph)
324 & e->rect(),
325 Qt::black);
326 if (contentsY()+contentsHeight() < visibleHeight())
327 p.fillRect (
328 QRect (clipx, contentsY()+contentsHeight(), clipw, visibleHeight()-contentsY()-contentsHeight())
329 & e->rect(),
330 Qt::black);
331
332 p.translate(visibleWidth() / 2.0, visibleHeight() / 2.0);
333 p.setWorldTransform(m_zoomMatrix, true);
334 const double ratio = 0.5 / m_coloredPixmap.devicePixelRatio();
335 p.translate(-m_coloredPixmap.width() * ratio, -m_coloredPixmap.height() * ratio);
336 p.drawPixmap(QPoint(clipx-contentsX(), clipy-contentsY()), m_coloredPixmap);
337 p.end();
338
339 if (m_showMouse)
340 paintMouseCursor(viewport(), calcMousePos (m_refreshSwitch));
341 }
342
343 /**
344 * Draws the mouse cursor according to the current selection of the type of
345 * mouse cursor to draw.
346 */
paintMouseCursor(QPaintDevice * dev,const QPoint & mousePos)347 void KMagZoomView::paintMouseCursor(QPaintDevice *dev, const QPoint &mousePos)
348 {
349 if(!dev)
350 return;
351
352 // painter for the zoom view
353 QPainter pz(dev);
354
355 // How to show the mouse :
356
357 switch(m_showMouse) {
358 case 1:
359 // 1. Square around the pixel
360 pz.setPen(Qt::white);
361 #ifdef __GNUC__
362 #warning "Port Qt4 pz.setRasterOp(Qt::XorROP);";
363 #endif
364 //pz.setRasterOp(Qt::XorROP);
365 pz.drawRect(mousePos.x()-1, mousePos.y()-1, (int)m_zoom+2, (int)m_zoom+2);
366 break;
367
368 case 2:
369 {
370 // 2. Arrow cursor
371 pz.setPen(Qt::black);
372 pz.setBackground(Qt::white);
373
374 QPixmap sCursor(16, 16);
375 QBitmap cursor = QBitmap::fromData( QSize(16, 16), left_ptr_bits);
376 QBitmap mask = QBitmap::fromData( QSize(16, 16), left_ptrmsk_bits);
377 sCursor.setMask(mask);
378 QPainter p(&sCursor);
379 p.setPen(Qt::gray);
380 p.drawPixmap(0, 0, mask);
381 p.setPen(Qt::black);
382 p.drawPixmap(0, 0, cursor);
383 p.end();
384 sCursor = sCursor.transformed(m_zoomMatrix);
385
386 // since hot spot is at 3,1
387 if (m_rotation == 0)
388 pz.drawPixmap(mousePos.x()-(int)(3.0*m_zoom), mousePos.y()-(int)m_zoom, sCursor);
389 else if (m_rotation == 90)
390 pz.drawPixmap(mousePos.x()-(int)(16.0*m_zoom), mousePos.y()-(int)(3.0*m_zoom), sCursor);
391 else if (m_rotation == 180)
392 pz.drawPixmap(mousePos.x()-(int)(13.0*m_zoom), mousePos.y()-(int)(16.0*m_zoom), sCursor);
393 else if (m_rotation == 270)
394 pz.drawPixmap(mousePos.x()-(int)m_zoom, mousePos.y()-(int)(13.0*m_zoom), sCursor);
395 }
396 break;
397
398 case 3:
399 {
400 // 3. Actual cursor
401 // Get the current cursor type
402 QWidget *dummy = QApplication::topLevelAt(QCursor::pos());
403 if(!dummy)
404 break;
405 switch(this->cursor().shape()) {
406 case Qt::ArrowCursor :
407 {
408 // 2. Arrow cursor
409 pz.setPen(Qt::black);
410 pz.setBackground(Qt::white);
411
412 QBitmap sCursor = QBitmap::fromData( QSize(16, 16), left_ptr_bits);
413 QBitmap mask = QBitmap::fromData( QSize(16, 16), left_ptrmsk_bits);
414 sCursor.setMask(mask);
415 sCursor = sCursor.transformed(m_zoomMatrix);
416
417 // since hot spot is at 3,1
418 pz.drawPixmap(mousePos.x()-(int)(3.0*m_zoom), mousePos.y()-(int)m_zoom, sCursor);
419 }
420 break;
421 default:
422 QBitmap sCursor = QBitmap::fromData( QSize(32, 32), phand_bits);
423 QBitmap mask = QBitmap::fromData( QSize(32, 32), phandm_bits);
424 sCursor.setMask(mask);
425
426 pz.drawPixmap(mousePos.x(), mousePos.y(), sCursor);
427 break;
428 } // switch(cursor)
429
430
431 }
432 break;
433
434 default:
435 // do not show anything
436 break;
437 } // switch(m_showMouse)
438 }
439
440
calcMousePos(bool updateMousePos)441 QPoint KMagZoomView::calcMousePos(bool updateMousePos)
442 {
443 // get position of mouse wrt selRect
444 if(updateMousePos) { // get a new position only if asked
445 m_latestCursorPos = QCursor::pos();
446 m_latestCursorPos -= QPoint(m_selRect.x(), m_selRect.y());
447 }
448
449 // get coordinates of the pixel w.r.t. the zoomed pixmap
450 if (m_rotation == 90)
451 return QPoint ((int)((float)(m_selRect.height()-m_latestCursorPos.y())*m_zoom),
452 (int)((float)m_latestCursorPos.x()*m_zoom));
453 else if (m_rotation == 180)
454 return QPoint ((int)((float)(m_selRect.width()-m_latestCursorPos.x())*m_zoom),
455 (int)((float)(m_selRect.height()-m_latestCursorPos.y())*m_zoom));
456 else if (m_rotation == 270)
457 return QPoint ((int)((float)m_latestCursorPos.y()*m_zoom),
458 (int)((float)(m_selRect.width()-m_latestCursorPos.x())*m_zoom));
459 else
460 return QPoint ((int)((float)m_latestCursorPos.x()*m_zoom),
461 (int)((float)m_latestCursorPos.y()*m_zoom));
462 }
463
464
465 // MOUSE ACTIONS
466
467 /**
468 * Called when mouse is clicked inside the window.
469 *
470 * @param e
471 */
mousePressEvent(QMouseEvent * e)472 void KMagZoomView::mousePressEvent(QMouseEvent *e)
473 {
474 switch(e->button()) {
475 case Qt::LeftButton :
476 if(m_ctrlKeyPressed) {
477 // check if currently in resize mode
478 // don't do anything if fitToWindow is enabled
479 if ((m_mouseMode != ResizeSelection) && !m_fitToWindow) {
480 // set the mode to ResizeSelection
481 m_mouseMode = ResizeSelection;
482
483 // set mouse cursor to "resize all direction"
484 setCursor(Qt::SizeAllCursor);
485
486 // backup the old position
487 m_oldMousePos.setX(e->globalX());
488 m_oldMousePos.setY(e->globalY());
489
490 // set the cursor position to the bottom-right of the selected region
491 QCursor::setPos(m_selRect.bottomRight());
492
493 // show the selection rectangle
494 m_selRect.show();
495 }
496 else {
497 // ignore this button press.. so it goes to the parent
498 e->ignore();
499 }
500 } else if(m_shiftKeyPressed) {
501 // check if currently in move mode
502 // don't do anything if follow mouse is enabled
503 if ((m_mouseMode != MoveSelection) && !m_followMouse) {
504 m_mouseMode = MoveSelection;
505
506 // set mouse cursor to cross hair
507 setCursor(Qt::CrossCursor);
508
509 // backup the old position
510 m_oldMousePos.setX(e->globalX());
511 m_oldMousePos.setY(e->globalY());
512
513 // set the cursor position to the center of the selected region
514 QCursor::setPos(m_selRect.center());
515
516 // show the selected rectangle
517 m_selRect.show();
518 }
519 else {
520 // ignore this button press.. so it goes to the parent
521 e->ignore();
522 }
523 } else {
524 // check if currently in move mode
525 // don't do anything if follow mouse is enabled
526 if ((m_mouseMode != GrabSelection) && !m_followMouse) {
527 m_mouseMode = GrabSelection;
528
529 // set mouse cursor to hand
530 setCursor(Qt::PointingHandCursor);
531
532 // store the old position
533 m_oldMousePos.setX(e->globalX());
534 m_oldMousePos.setY(e->globalY());
535
536 m_oldCenter = m_selRect.center();
537
538 // show the selected rectangle
539 m_selRect.show();
540 }
541 else {
542 // ignore this button press.. so it goes to the parent
543 e->ignore();
544 }
545 }
546 break;
547
548 case Qt::MiddleButton :
549 // check if currently in move mode
550 // don't do anything if follow mouse is enabled
551 if ((m_mouseMode != MoveSelection) && !m_followMouse) {
552 m_mouseMode = MoveSelection;
553
554 // set mouse cursor to cross hair
555 setCursor(Qt::CrossCursor);
556
557 // backup the old position
558 m_oldMousePos.setX(e->globalX());
559 m_oldMousePos.setY(e->globalY());
560
561 // set the cursor position to the center of the selected region
562 QCursor::setPos(m_selRect.center());
563
564 // show the selected rectangle
565 m_selRect.show();
566 }
567 else {
568 // ignore this button press.. so it goes to the parent
569 e->ignore();
570 }
571 break;
572 // do nothing
573 default:
574 // ignore this button press.. so it goes to the parent
575 e->ignore();
576 break;
577 }
578 }
579
580
581 /**
582 * Called when a mouse button is released
583 *
584 * @param e
585 */
mouseReleaseEvent(QMouseEvent * e)586 void KMagZoomView::mouseReleaseEvent(QMouseEvent *e)
587 {
588 switch(e->button()) {
589 case Qt::LeftButton :
590 case Qt::MiddleButton :
591 // check if currently in move mode
592 if(m_mouseMode == MoveSelection) {
593 // hide the selection window
594 m_selRect.hide();
595 // set the mouse mode to normal
596 m_mouseMode = Normal;
597
598 // restore the cursor shape
599 setCursor(Qt::ArrowCursor);
600
601 // restore the cursor position
602 QCursor::setPos(m_oldMousePos);
603 } else if(m_mouseMode == ResizeSelection) {
604 // hide the selection window
605 m_selRect.hide();
606 // set the mouse mode to normal
607 m_mouseMode = Normal;
608
609 // restore the cursor shape
610 setCursor(Qt::ArrowCursor);
611
612 // restore the cursor position
613 QCursor::setPos(m_oldMousePos);
614 } else if(m_mouseMode == GrabSelection) {
615 // hide the selection window
616 m_selRect.hide();
617
618 // set the mouse mode to normal
619 m_mouseMode = Normal;
620
621 // restore the cursor shape
622 setCursor(Qt::ArrowCursor);
623 }
624 break;
625
626 case Qt::RightButton :
627 break;
628 case Qt::NoButton :
629 break;
630
631 // do nothing
632 default:
633 ;
634 }
635 }
636
637
638 /**
639 * Called when mouse is moved inside the window
640 *
641 * @param e
642 */
mouseMoveEvent(QMouseEvent * e)643 void KMagZoomView::mouseMoveEvent(QMouseEvent *e)
644 {
645 if(m_mouseMode == ResizeSelection) {
646 // In resize selection mode
647 // set the current mouse position as the bottom, right corner
648 m_selRect.setRight(e->globalX());
649 m_selRect.setBottom(e->globalY());
650 m_selRect.update();
651 grabFrame();
652 } else if(m_mouseMode == MoveSelection) {
653 QPoint newCenter;
654
655 // set new center to be the current mouse position
656 newCenter = e->globalPos();
657
658 // make sure the mouse position is not taking the grab window outside
659 // the display
660 if(newCenter.x() < m_selRect.width()/2) {
661 // set X to the minimum possible X
662 newCenter.setX(m_selRect.width()/2);
663 } else if(newCenter.x() >= QApplication::desktop()->width()-m_selRect.width()/2) {
664 // set X to the maximum possible X
665 newCenter.setX(QApplication::desktop()->width()-m_selRect.width()/2-1);
666 }
667
668 if(newCenter.y() < m_selRect.height()/2) {
669 // set Y to the minimum possible Y
670 newCenter.setY(m_selRect.height()/2);
671 } else if(newCenter.y() >= QApplication::desktop()->height()-m_selRect.height()/2) {
672 // set Y to the maximum possible Y
673 newCenter.setY(QApplication::desktop()->height()-m_selRect.height()/2-1);
674 }
675 // move to the new center
676 m_selRect.moveCenter(newCenter);
677 // update the grab rectangle display
678 m_selRect.update();
679 grabFrame();
680 } else if(m_mouseMode == GrabSelection) {
681 QPoint newPos;
682
683 // get new position
684 newPos = e->globalPos();
685
686 QPoint delta = (newPos - m_oldMousePos)/m_zoom;
687 QPoint newCenter = m_oldCenter-delta;
688
689 // make sure the mouse position is not taking the grab window outside
690 // the display
691 if(newCenter.x() < m_selRect.width()/2) {
692 // set X to the minimum possible X
693 newCenter.setX(m_selRect.width()/2);
694 } else if(newCenter.x() >= QApplication::desktop()->width()-m_selRect.width()/2) {
695 // set X to the maximum possible X
696 newCenter.setX(QApplication::desktop()->width()-m_selRect.width()/2-1);
697 }
698
699 if(newCenter.y() < m_selRect.height()/2) {
700 // set Y to the minimum possible Y
701 newCenter.setY(m_selRect.height()/2);
702 } else if(newCenter.y() >= QApplication::desktop()->height()-m_selRect.height()/2) {
703 // set Y to the maximum possible Y
704 newCenter.setY(QApplication::desktop()->height()-m_selRect.height()/2-1);
705 }
706
707 // move to the new center
708 m_selRect.moveCenter(newCenter);
709 // update the grab rectangle display
710 m_selRect.update();
711 grabFrame();
712 }
713 }
714
keyPressEvent(QKeyEvent * e)715 void KMagZoomView::keyPressEvent(QKeyEvent *e)
716 {
717 int offset = 16;
718 if (e->modifiers() & Qt::ShiftModifier)
719 offset = 1;
720
721 if (e->key() == Qt::Key_Control)
722 m_ctrlKeyPressed = true;
723 else if (e->key() == Qt::Key_Shift)
724 m_shiftKeyPressed = true;
725 else if (e->key() == Qt::Key_Left)
726 {
727 if (e->modifiers() & Qt::ControlModifier)
728 {
729 if (offset >= m_selRect.width())
730 m_selRect.setWidth (1);
731 else
732 m_selRect.setWidth (m_selRect.width()-offset);
733 }
734 else if (contentsX() > 0)
735 {
736 offset = (int)(offset*m_zoom);
737 if (contentsX() > offset)
738 setContentsPos (contentsX()-offset, contentsY());
739 else
740 setContentsPos (0, contentsY());
741 }
742 else if (m_followMouse == false)
743 {
744 if (offset > m_selRect.x())
745 m_selRect.setX (0);
746 else
747 m_selRect.translate (-offset,0);
748 }
749 m_selRect.update();
750 }
751 else if (e->key() == Qt::Key_Right)
752 {
753 if (e->modifiers() & Qt::ControlModifier)
754 {
755 if (m_selRect.right()+offset >= QApplication::desktop()->width())
756 m_selRect.setRight (QApplication::desktop()->width()-1);
757 else
758 m_selRect.setRight (m_selRect.right()+offset);
759 }
760 else if (contentsX() < contentsWidth()-visibleWidth())
761 {
762 offset = (int)(offset*m_zoom);
763 if (contentsX()+offset < contentsWidth()-visibleWidth())
764 setContentsPos (contentsX()+offset, contentsY());
765 else
766 setContentsPos (contentsWidth()-visibleWidth(), contentsY());
767 }
768 else if (m_followMouse == false)
769 {
770 if (m_selRect.right()+offset >= QApplication::desktop()->width())
771 m_selRect.moveTopRight (QPoint (QApplication::desktop()->width()-1, m_selRect.top()));
772 else
773 m_selRect.translate (offset,0);
774 }
775 m_selRect.update();
776 }
777 else if (e->key() == Qt::Key_Up)
778 {
779 if (e->modifiers() & Qt::ControlModifier)
780 {
781 if (offset >= m_selRect.height())
782 m_selRect.setHeight (1);
783 else
784 m_selRect.setHeight (m_selRect.height()-offset);
785 }
786 else if (contentsY() > 0)
787 {
788 offset = (int)(offset*m_zoom);
789 if (contentsY() > offset)
790 setContentsPos (contentsX(), contentsY()-offset);
791 else
792 setContentsPos (contentsX(), 0);
793 }
794 else if (m_followMouse == false)
795 {
796 if (offset > m_selRect.y())
797 m_selRect.setY (0);
798 else
799 m_selRect.translate (0, -offset);
800 }
801 m_selRect.update();
802 }
803 else if (e->key() == Qt::Key_Down)
804 {
805 if (e->modifiers() & Qt::ControlModifier)
806 {
807 if (m_selRect.bottom()+offset >= QApplication::desktop()->height())
808 m_selRect.setBottom (QApplication::desktop()->height()-1);
809 else
810 m_selRect.setBottom (m_selRect.bottom()+offset);
811 }
812 else if (contentsY() < contentsHeight()-visibleHeight())
813 {
814 offset = (int)(offset*m_zoom);
815 if (contentsY()+offset < contentsHeight()-visibleHeight())
816 setContentsPos (contentsX(), contentsY()+offset);
817 else
818 setContentsPos (contentsX(), contentsHeight()-visibleHeight());
819 }
820 else if (m_followMouse == false)
821 {
822 if (m_selRect.bottom()+offset >= QApplication::desktop()->height())
823 m_selRect.moveBottomLeft (QPoint (m_selRect.left(), QApplication::desktop()->height()-1));
824 else
825 m_selRect.translate (0, offset);
826 }
827 m_selRect.update();
828 }
829 else
830 e->ignore();
831 }
832
keyReleaseEvent(QKeyEvent * e)833 void KMagZoomView::keyReleaseEvent(QKeyEvent *e)
834 {
835 if (e->key() == Qt::Key_Control)
836 m_ctrlKeyPressed = false;
837 else if (e->key() == Qt::Key_Shift)
838 m_shiftKeyPressed = false;
839 else
840 e->ignore();
841 }
842
focusOutEvent(QFocusEvent * e)843 void KMagZoomView::focusOutEvent(QFocusEvent *e)
844 {
845 if(e->lostFocus() == true) {
846 m_ctrlKeyPressed = false;
847 m_shiftKeyPressed = false;
848 }
849 }
850
851 // SLOTS
852
853 /**
854 * This will fit the zoom view to the view window, thus using the maximum
855 * possible space in the window.
856 */
fitToWindow()857 void KMagZoomView::fitToWindow()
858 {
859 unsigned int newWidth, newHeight;
860
861 // this is a temporary solution, cast, maybe newWidth and newHeight should be float
862 if ((m_rotation == 90) || (m_rotation == 270))
863 {
864 newWidth = static_cast<unsigned int>((visibleHeight() + m_zoom - 1) / m_zoom);
865 newHeight = static_cast<unsigned int>((visibleWidth() + m_zoom - 1) / m_zoom);
866 } else {
867 newWidth = static_cast<unsigned int>((visibleWidth() + m_zoom - 1) / m_zoom);
868 newHeight = static_cast<unsigned int>((visibleHeight() + m_zoom - 1) / m_zoom);
869 }
870
871 QPoint currCenter = m_selRect.center();
872
873 m_selRect.setWidth(newWidth);
874 m_selRect.setHeight(newHeight);
875
876 // make sure the selection window does not go outside of the display
877 if(currCenter.x() < m_selRect.width()/2) {
878 // set X to the minimum possible X
879 currCenter.setX(m_selRect.width()/2);
880 } else if(currCenter.x() >= QApplication::desktop()->width()-m_selRect.width()/2) {
881 // set X to the maximum possible X
882 currCenter.setX(QApplication::desktop()->width()-m_selRect.width()/2-1);
883 }
884
885 if(currCenter.y() < m_selRect.height()/2) {
886 // set Y to the minimum possible Y
887 currCenter.setY(m_selRect.height()/2);
888 } else if(currCenter.y() >= QApplication::desktop()->height()-m_selRect.height()/2) {
889 // set Y to the maximum possible Y
890 currCenter.setY(QApplication::desktop()->height()-m_selRect.height()/2-1);
891 }
892
893 m_selRect.moveCenter(currCenter);
894 // update the grab rectangle display
895 m_selRect.update();
896 // m_fitToWindow = true;
897 viewport()->update();
898 }
899
setFitToWindow(bool fit)900 void KMagZoomView::setFitToWindow(bool fit)
901 {
902 m_fitToWindow = fit;
903 if (fit)
904 fitToWindow();
905 }
906
907
908 /**
909 * Grabs frame from X
910 */
grabFrame()911 void KMagZoomView::grabFrame()
912 {
913 // check refresh status
914 if (!m_refreshSwitch)
915 return;
916
917 // check if follow-mouse or follow-focus are enabled
918 if((m_followMouse || m_followFocus) && (m_mouseMode != ResizeSelection)) {
919 // center-position of the grab-area
920 QPoint newCenter;
921
922 if(m_followMouse) {
923 // set new center to be the current mouse position
924 newCenter = QCursor::pos();
925 #ifdef QAccessibilityClient_FOUND
926 } else if(m_followFocus) {
927 // set the new center to the current keyboard cursor position
928 newCenter = m_oldFocus;
929 if(m_followBoth) {
930 m_followFocus=false;
931 m_followMouse=true;
932 }
933 #endif
934 }
935
936 // make sure the mouse position is not taking the grab window outside
937 // the display
938 if(newCenter.x() < m_selRect.width()/2) {
939 // set X to the minimum possible X
940 newCenter.setX(m_selRect.width()/2);
941 } else if(newCenter.x() >= QApplication::desktop()->width()-m_selRect.width()/2) {
942 // set X to the maximum possible X
943 newCenter.setX(QApplication::desktop()->width()-m_selRect.width()/2-1);
944 }
945
946 if(newCenter.y() < m_selRect.height()/2) {
947 // set Y to the minimum possible Y
948 newCenter.setY(m_selRect.height()/2);
949 } else if(newCenter.y() >= QApplication::desktop()->height()-m_selRect.height()/2) {
950 // set Y to the maximum possible Y
951 newCenter.setY(QApplication::desktop()->height()-m_selRect.height()/2-1);
952 }
953 // move to the new center
954 m_selRect.moveCenter(newCenter);
955
956 // update the grab rectangle display
957 m_selRect.update();
958 }
959
960 // define a normalized rectangle
961 QRect selRect = m_selRect.normalized();
962
963 // grab screenshot from the screen and put it in the pixmap
964 QScreen *screen = qApp->primaryScreen(); // ## How to select the right screen?
965 m_coloredPixmap = screen->grabWindow(QApplication::desktop()->winId(), selRect.x(), selRect.y(),
966 selRect.width(), selRect.height());
967
968 // colorize the grabbed pixmap
969 if (m_colormode != 0)
970 m_coloredPixmap = QPixmap::fromImage(ColorSim::recolor(m_coloredPixmap.toImage(), m_colormode));
971
972 // erase background covered by kmag view ...
973 QRect viewRect = rect();
974 viewRect.translate(mapTo(window(), QPoint(0, 0)));
975 viewRect.translate(-selRect.topLeft());
976 viewRect.translate(window()->geometry().topLeft());
977 QRegion region(viewRect);
978
979 // ... but exclude own popups ...
980 const QList<QWidget *> siblings = QApplication::topLevelWidgets();
981 for (QWidget *sibling : siblings) {
982 if (sibling != window() && (sibling->windowType() & Qt::Window) && sibling->isVisible()) {
983 QRect rect = sibling->frameGeometry();
984 rect.translate(-selRect.topLeft());
985 region -= rect;
986 }
987 }
988
989 QPainter p(&m_coloredPixmap);
990 for (const QRect &rect : region) {
991 p.fillRect(rect, palette().dark());
992 }
993 p.end();
994
995 QRect r = m_zoomMatrix.mapRect(m_coloredPixmap.rect());
996 // call repaint to display the newly grabbed image
997 horizontalScrollBar()->setPageStep(r.width());
998 verticalScrollBar()->setPageStep(r.height());
999 viewport()->update();
1000 }
1001
1002
1003 /**
1004 * Updates the mouse cursor in the zoom view.
1005 */
updateMouseView()1006 void KMagZoomView::updateMouseView()
1007 {
1008 if (m_fps < 8)
1009 viewport()->update();
1010 }
1011
1012 /**
1013 * Toggles the state of refreshing.
1014 */
toggleRefresh()1015 void KMagZoomView::toggleRefresh()
1016 {
1017 if(m_refreshSwitch) {
1018 m_refreshSwitch = false;
1019 m_grabTimer.stop();
1020 m_mouseViewTimer.stop();
1021 } else {
1022 m_refreshSwitch = true;
1023 m_grabTimer.start(1000/m_fps);
1024 m_mouseViewTimer.start(40);
1025 }
1026 }
1027
1028 /**
1029 * This function sets the zoom value to be used.
1030 */
setZoom(float zoom)1031 void KMagZoomView::setZoom(float zoom)
1032 {
1033 // use this zoom
1034 m_zoom = zoom;
1035
1036 // update selection window size when zooming in if necessary
1037 if (m_fitToWindow)
1038 fitToWindow();
1039
1040 // recompute the zoom matrix
1041 setupMatrix();
1042
1043 viewport()->update();
1044 }
1045
1046 /**
1047 * This function sets the rotation value to be used.
1048 */
setRotation(int rotation)1049 void KMagZoomView::setRotation(int rotation)
1050 {
1051 // use this rotation
1052 m_rotation = rotation;
1053
1054 // update selection window size if necessary
1055 if (m_fitToWindow)
1056 fitToWindow();
1057
1058 // recompute the zoom matrix
1059 setupMatrix();
1060
1061 viewport()->update();
1062 }
1063
1064 /**
1065 * Set a new color simulation mode.
1066 */
setColorMode(int mode)1067 void KMagZoomView::setColorMode(int mode)
1068 {
1069 if (m_colormode != mode) {
1070 m_colormode = mode;
1071 viewport()->update();
1072 }
1073 }
1074
1075 /**
1076 * Set a new refresh rate.
1077 */
setRefreshRate(float fps)1078 void KMagZoomView::setRefreshRate(float fps)
1079 {
1080 if(fps < 0.1)
1081 return;
1082 m_fps = static_cast<unsigned int>(fps);
1083
1084 if(m_grabTimer.isActive())
1085 m_grabTimer.start(static_cast<int>(1000.0/m_fps));
1086 }
1087
showSelRect(bool show)1088 void KMagZoomView::showSelRect(bool show)
1089 {
1090 m_selRect.alwaysVisible(show);
1091 if(show) {
1092 m_selRect.show();
1093 } else if(m_mouseMode == Normal) {
1094 m_selRect.hide();
1095 }
1096 }
1097
1098 /**
1099 * Sets the selection rectangle to the given position.
1100 */
setSelRectPos(const QRect & rect)1101 void KMagZoomView::setSelRectPos(const QRect & rect)
1102 {
1103 m_selRect.setRect(rect.x(), rect.y(), rect.width(), rect.height());
1104 m_selRect.update();
1105 grabFrame();
1106 }
1107
showMouse(unsigned int type)1108 bool KMagZoomView::showMouse(unsigned int type)
1109 {
1110 if(int(type) > m_showMouseTypes.count()-1)
1111 return (false);
1112 else
1113 m_showMouse = type;
1114
1115 return(true);
1116 }
1117
getShowMouseType() const1118 unsigned int KMagZoomView::getShowMouseType() const
1119 {
1120 return (m_showMouse);
1121 }
1122
getShowMouseStringList() const1123 QStringList KMagZoomView::getShowMouseStringList() const
1124 {
1125 return (m_showMouseTypes);
1126 }
1127
1128
1129 /**
1130 * Returns the image which is being displayed. It's again drawn by adding
1131 * the mouse cursor if needed.
1132 */
getImage()1133 QImage KMagZoomView::getImage()
1134 {
1135 QImage image = m_coloredPixmap.transformed(m_zoomMatrix).toImage();
1136
1137 // show the pixel under mouse cursor
1138 if(m_showMouse && !image.isNull()) {
1139 // paint the mouse cursor w/o updating to a newer position
1140 paintMouseCursor(&image, calcMousePos(false));
1141 }
1142 return(image);
1143 }
1144