File indexing completed on 2024-04-28 03:40:29

0001 // SPDX-FileCopyrightText: 2001-2003 Sarang Lakare <sarang@users.sourceforge.net>
0002 // SPDX-FileCopyrightText: 2003-2004 Olaf Schmidt <ojschmidt@kde.org>
0003 // SPDX-FileCopyrightText: 2008 Matthew Woehlke <mw_triad@users.sourceforge.net>
0004 // SPDX-FileCopyrightText: 2010 Sebastian Sauer <sebsauer@kdab.com>
0005 // SPDX-License-Identifier: GPL-2.0-or-later
0006 
0007 // application specific includes
0008 #include "kmagzoomview.h"
0009 #include "colorsim.h"
0010 
0011 // include files for Qt
0012 #include <QApplication>
0013 #include <QBitmap>
0014 #include <QScrollBar>
0015 #include <QScreen>
0016 #include <QPainter>
0017 
0018 // include files for KF5
0019 #include <KLocalizedString>
0020 #include <chrono>
0021 
0022 using namespace std::chrono_literals;
0023 
0024 #if HAVE_QACCESSIBILITYCLIENT
0025 #include <qaccessibilityclient/accessibleobject.h>
0026 #endif
0027 
0028 // include bitmaps for cursors
0029 static const uchar left_ptr_bits[] = {
0030    0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00,
0031    0xf8, 0x01, 0xf8, 0x03, 0xf8, 0x07, 0xf8, 0x00, 0xd8, 0x00, 0x88, 0x01,
0032    0x80, 0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00};
0033 
0034 static const uchar left_ptrmsk_bits[] = {
0035    0x0c, 0x00, 0x1c, 0x00, 0x3c, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xfc, 0x01,
0036    0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x01, 0xdc, 0x03,
0037    0xcc, 0x03, 0x80, 0x07, 0x80, 0x07, 0x00, 0x03};
0038 
0039 static const uchar phand_bits[] = {
0040   0x00, 0x00, 0x00, 0x00,  0xfe, 0x01, 0x00, 0x00,  0x01, 0x02, 0x00, 0x00,
0041   0x7e, 0x04, 0x00, 0x00,  0x08, 0x08, 0x00, 0x00,  0x70, 0x08, 0x00, 0x00,
0042   0x08, 0x08, 0x00, 0x00,  0x70, 0x14, 0x00, 0x00,  0x08, 0x22, 0x00, 0x00,
0043   0x30, 0x41, 0x00, 0x00,  0xc0, 0x20, 0x00, 0x00,  0x40, 0x12, 0x00, 0x00,
0044   0x80, 0x08, 0x00, 0x00,  0x00, 0x05, 0x00, 0x00,  0x00, 0x02, 0x00, 0x00,
0045   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0046   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0047   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0048   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0049   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0050   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00 };
0051 static const uchar phandm_bits[] = {
0052   0xfe, 0x01, 0x00, 0x00,  0xff, 0x03, 0x00, 0x00,  0xff, 0x07, 0x00, 0x00,
0053   0xff, 0x0f, 0x00, 0x00,  0xfe, 0x1f, 0x00, 0x00,  0xf8, 0x1f, 0x00, 0x00,
0054   0xfc, 0x1f, 0x00, 0x00,  0xf8, 0x3f, 0x00, 0x00,  0xfc, 0x7f, 0x00, 0x00,
0055   0xf8, 0xff, 0x00, 0x00,  0xf0, 0x7f, 0x00, 0x00,  0xe0, 0x3f, 0x00, 0x00,
0056   0xc0, 0x1f, 0x00, 0x00,  0x80, 0x0f, 0x00, 0x00,  0x00, 0x07, 0x00, 0x00,
0057   0x00, 0x02, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0058   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0059   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0060   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0061   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00,
0062   0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00 };
0063 
0064 
0065 KMagZoomView::KMagZoomView(QWidget *parent, const char *name)
0066   : QAbstractScrollArea(parent),
0067     m_selRect(0, 0, 128, 128, this),
0068     m_grabTimer(parent),
0069     m_mouseViewTimer(parent),
0070     m_latestCursorPos(0,0),
0071     m_followMouse(false),
0072     m_followFocus(false),
0073     m_showMouse(1),
0074     m_zoom(1.0),
0075     m_rotation(0),
0076     m_colormode(0),
0077     m_fitToWindow(true)
0078 {
0079   setObjectName( QLatin1String( name ));
0080 
0081   viewport()->setMouseTracking(true);
0082   viewport()->setAttribute(Qt::WA_NoSystemBackground, true);
0083   viewport()->setAutoFillBackground(false);
0084   viewport()->setFocusPolicy(Qt::StrongFocus);
0085 
0086   // init the zoom matrix
0087   setupMatrix();
0088 
0089   m_ctrlKeyPressed = false;
0090   m_shiftKeyPressed = false;
0091   m_refreshSwitch = true;
0092   m_refreshSwitchStateOnHide = m_refreshSwitch;
0093 
0094   // set the refresh rate
0095   setRefreshRate(10);
0096 
0097   // connect it to grabFrame()
0098   connect(&m_grabTimer, &QTimer::timeout, this, &KMagZoomView::grabFrame);
0099   // start the grabTimer
0100   m_grabTimer.start(static_cast<int>(1000.0/m_fps));
0101 
0102   // connect it to updateMouseView()
0103   connect(&m_mouseViewTimer, &QTimer::timeout, this, &KMagZoomView::updateMouseView);
0104   // start the grabTimer @ 25 frames per second!
0105   m_mouseViewTimer.start(40ms);
0106 
0107   this->setWhatsThis( i18n("This is the main window which shows the contents of the\
0108  selected region. The contents will be magnified according to the zoom level that is set."));
0109 
0110   // different ways to show the cursor.
0111   m_showMouseTypes << QStringLiteral( "Hidden" ) << QStringLiteral( "Box" ) << QStringLiteral( "Arrow" ) << QStringLiteral( "Actual" );
0112 
0113   if(m_fitToWindow)
0114     fitToWindow();
0115 
0116 #if HAVE_QACCESSIBILITYCLIENT
0117   //subscribe to focus events from registry
0118   m_registry.subscribeEventListeners(QAccessibleClient::Registry::Focus | QAccessibleClient::Registry::TextCaretMoved);
0119 #endif
0120 }
0121 
0122 KMagZoomView::~KMagZoomView()
0123 {
0124 }
0125 
0126 int KMagZoomView::contentsX() const
0127 {
0128   return horizontalScrollBar()->value();
0129 }
0130 
0131 int KMagZoomView::contentsY() const
0132 {
0133   return verticalScrollBar()->value();
0134 }
0135 
0136 int KMagZoomView::contentsWidth() const
0137 {
0138   return horizontalScrollBar()->pageStep();
0139 }
0140 
0141 int KMagZoomView::contentsHeight() const
0142 {
0143   return verticalScrollBar()->pageStep();
0144 }
0145 
0146 int KMagZoomView::visibleWidth() const
0147 {
0148   return viewport()->width();
0149 }
0150 
0151 int KMagZoomView::visibleHeight() const
0152 {
0153   return viewport()->height();
0154 }
0155 
0156 void KMagZoomView::setContentsPos(int x, int y)
0157 {
0158   horizontalScrollBar()->setValue(x);
0159   verticalScrollBar()->setValue(y);
0160 }
0161 
0162 void KMagZoomView::setupMatrix()
0163 {
0164   m_zoomMatrix.reset();
0165   m_zoomMatrix.scale(m_zoom, m_zoom);
0166   m_zoomMatrix.rotate(m_rotation);
0167 }
0168 
0169 /**
0170  * This function will set/reset mouse following of grab window.
0171  */
0172 void KMagZoomView::followMouse(bool follow)
0173 {
0174   m_followMouse = follow;
0175   m_mouseMode = Normal;
0176   if(follow) {
0177     setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
0178     setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
0179   } else {
0180     setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
0181     setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
0182   }
0183 }
0184 
0185 #if HAVE_QACCESSIBILITYCLIENT
0186 
0187 void KMagZoomView::followBoth(bool follow)
0188 {
0189     m_followBoth = follow;
0190     if(follow){
0191         m_followMouse = true;
0192         m_followFocus = false;
0193         setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
0194         setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
0195 
0196         connect(&m_registry, SIGNAL(focusChanged(QAccessibleClient::AccessibleObject)),
0197                 this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
0198         connect(&m_registry, SIGNAL(textCaretMoved(QAccessibleClient::AccessibleObject,int)),
0199                 this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
0200     } else {
0201         setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
0202         setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
0203 
0204         disconnect(this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
0205     }
0206 }
0207 
0208 /**
0209  * This function will set/reset keyboard focus following of grab window.
0210  */
0211 void KMagZoomView::followFocus(bool follow)
0212 {
0213   if(m_followFocus == follow)
0214       return;
0215   m_followFocus = follow;
0216   m_mouseMode = Normal;
0217   if(follow) {
0218     setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
0219     setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
0220 
0221     connect(&m_registry,SIGNAL(focusChanged(QAccessibleClient::AccessibleObject)),
0222             this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
0223     connect(&m_registry, SIGNAL(textCaretMoved(QAccessibleClient::AccessibleObject,int)),
0224             this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
0225   } else {
0226     setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
0227     setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOn);
0228 
0229     disconnect(this, SLOT(focusChanged(QAccessibleClient::AccessibleObject)));
0230   }
0231 }
0232 
0233 void KMagZoomView::focusChanged(const QAccessibleClient::AccessibleObject &object)
0234 {
0235     m_oldFocus = object.focusPoint();
0236     if(m_followBoth && !m_selRect.contains(m_oldFocus)) {
0237         QCursor::setPos(m_oldFocus);
0238         m_followFocus = true;
0239         m_followMouse = false;
0240     }
0241 }
0242 
0243 #endif
0244 
0245 /**
0246  * Called when the widget is hidden. Stop refresh when this happens.
0247  */
0248 void KMagZoomView::hideEvent( QHideEvent* )
0249 {
0250   // Save the state of the refresh switch.. the state will be restored
0251   // when showEvent is called
0252   m_refreshSwitchStateOnHide = m_refreshSwitch;
0253 
0254   // Check if refresh is ON
0255   if(m_refreshSwitch) {
0256     toggleRefresh();
0257   }
0258 }
0259 
0260 
0261 /**
0262  * Called when the widget is shown. Start refresh when this happens.
0263  */
0264 void KMagZoomView::showEvent( QShowEvent* )
0265 {
0266   // Check if refresh switch was ON when hide was called and if currently it is OFF
0267   if(m_refreshSwitchStateOnHide && !m_refreshSwitch) {
0268     // start the refresh in that case
0269     toggleRefresh();
0270   }
0271 }
0272 
0273 /**
0274  * Called when the widget is resized. Check if fitToWindow is active when this happens.
0275  */
0276 void KMagZoomView::resizeEvent( QResizeEvent * e )
0277 {
0278   horizontalScrollBar()->setRange(0, contentsWidth() - visibleWidth());
0279   verticalScrollBar()->setRange(0, contentsHeight() - visibleHeight());
0280   QAbstractScrollArea::resizeEvent(e);
0281   if(m_fitToWindow)
0282     fitToWindow();
0283 }
0284 
0285 /**
0286  * Called when the widget is to be repainted.
0287  *
0288  * @param p
0289  */
0290 void KMagZoomView::paintEvent(QPaintEvent *e)
0291 {
0292   if(m_coloredPixmap.isNull())
0293     return;
0294 
0295   QPainter p(viewport());
0296   int clipx = e->rect().x();
0297   int clipy = e->rect().x();
0298   int clipw = e->rect().width();
0299   int cliph = e->rect().height();
0300 
0301   // Paint empty areas Qt::black
0302   if (contentsX()+contentsWidth() < visibleWidth())
0303     p.fillRect (
0304         QRect (contentsX()+contentsWidth(), clipy, visibleWidth()-contentsX()-contentsWidth(), cliph)
0305         & e->rect(),
0306         Qt::black);
0307   if (contentsY()+contentsHeight() < visibleHeight())
0308     p.fillRect (
0309         QRect (clipx, contentsY()+contentsHeight(), clipw, visibleHeight()-contentsY()-contentsHeight())
0310         & e->rect(),
0311         Qt::black);
0312 
0313   p.translate(visibleWidth() / 2.0, visibleHeight() / 2.0);
0314   p.setWorldTransform(m_zoomMatrix, true);
0315   const double ratio = 0.5 / m_coloredPixmap.devicePixelRatio();
0316   p.translate(-m_coloredPixmap.width() * ratio, -m_coloredPixmap.height() * ratio);
0317   p.drawPixmap(QPoint(clipx-contentsX(), clipy-contentsY()), m_coloredPixmap);
0318   p.end();
0319 
0320   if (m_showMouse)
0321     paintMouseCursor(viewport(), calcMousePos (m_refreshSwitch));
0322 }
0323 
0324 /**
0325  * Draws the mouse cursor according to the current selection of the type of
0326  * mouse cursor to draw.
0327  */
0328 void KMagZoomView::paintMouseCursor(QPaintDevice *dev, const QPoint &mousePos)
0329 {
0330   if(!dev)
0331     return;
0332 
0333   // painter for the zoom view
0334   QPainter pz(dev);
0335 
0336     // How to show the mouse :
0337 
0338     switch(m_showMouse) {
0339     case 1:
0340       // 1. Square around the pixel
0341       pz.setPen(Qt::white);
0342 #ifdef __GNUC__
0343 #warning "Port Qt4 pz.setRasterOp(Qt::XorROP);";
0344 #endif
0345       //pz.setRasterOp(Qt::XorROP);
0346       pz.drawRect(mousePos.x()-1, mousePos.y()-1, (int)m_zoom+2, (int)m_zoom+2);
0347       break;
0348 
0349     case 2:
0350     {
0351       // 2. Arrow cursor
0352       pz.setPen(Qt::black);
0353       pz.setBackground(Qt::white);
0354 
0355       QPixmap sCursor(16, 16);
0356       QBitmap cursor = QBitmap::fromData( QSize(16,  16),  left_ptr_bits);
0357       QBitmap mask = QBitmap::fromData( QSize(16,  16),  left_ptrmsk_bits);
0358       sCursor.setMask(mask);
0359       QPainter p(&sCursor);
0360       p.setPen(Qt::gray);
0361       p.drawPixmap(0, 0, mask);
0362       p.setPen(Qt::black);
0363       p.drawPixmap(0, 0, cursor);
0364       p.end();
0365       sCursor = sCursor.transformed(m_zoomMatrix);
0366 
0367       // since hot spot is at 3,1
0368       if (m_rotation == 0)
0369         pz.drawPixmap(mousePos.x()-(int)(3.0*m_zoom), mousePos.y()-(int)m_zoom, sCursor);
0370       else if (m_rotation == 90)
0371         pz.drawPixmap(mousePos.x()-(int)(16.0*m_zoom), mousePos.y()-(int)(3.0*m_zoom), sCursor);
0372       else if (m_rotation == 180)
0373         pz.drawPixmap(mousePos.x()-(int)(13.0*m_zoom), mousePos.y()-(int)(16.0*m_zoom), sCursor);
0374       else if (m_rotation == 270)
0375         pz.drawPixmap(mousePos.x()-(int)m_zoom, mousePos.y()-(int)(13.0*m_zoom), sCursor);
0376     }
0377     break;
0378 
0379     case 3:
0380     {
0381       // 3. Actual cursor
0382       // Get the current cursor type
0383       QWidget *dummy  = QApplication::topLevelAt(QCursor::pos());
0384       if(!dummy)
0385         break;
0386       switch(this->cursor().shape())  {
0387               case Qt::ArrowCursor :
0388          {
0389           // 2. Arrow cursor
0390           pz.setPen(Qt::black);
0391           pz.setBackground(Qt::white);
0392 
0393           QBitmap sCursor = QBitmap::fromData( QSize(16,  16),  left_ptr_bits);
0394           QBitmap mask = QBitmap::fromData( QSize(16,  16),  left_ptrmsk_bits);
0395           sCursor.setMask(mask);
0396           sCursor = sCursor.transformed(m_zoomMatrix);
0397 
0398           // since hot spot is at 3,1
0399           pz.drawPixmap(mousePos.x()-(int)(3.0*m_zoom), mousePos.y()-(int)m_zoom, sCursor);
0400         }
0401         break;
0402         default:
0403           QBitmap sCursor = QBitmap::fromData( QSize(32,  32),  phand_bits);
0404           QBitmap mask = QBitmap::fromData( QSize(32,  32),  phandm_bits);
0405           sCursor.setMask(mask);
0406 
0407           pz.drawPixmap(mousePos.x(), mousePos.y(), sCursor);
0408         break;
0409       } // switch(cursor)
0410 
0411 
0412     }
0413     break;
0414 
0415     default:
0416       // do not show anything
0417       break;
0418     } // switch(m_showMouse)
0419 }
0420 
0421 
0422 QPoint KMagZoomView::calcMousePos(bool updateMousePos)
0423 {
0424   // get position of mouse wrt selRect
0425   if(updateMousePos) { // get a new position only if asked
0426     m_latestCursorPos = QCursor::pos();
0427     m_latestCursorPos -= QPoint(m_selRect.x(), m_selRect.y());
0428   }
0429 
0430   // get coordinates of the pixel w.r.t. the zoomed pixmap
0431   if (m_rotation == 90)
0432     return QPoint ((int)((float)(m_selRect.height()-m_latestCursorPos.y())*m_zoom),
0433                    (int)((float)m_latestCursorPos.x()*m_zoom));
0434   else if (m_rotation == 180)
0435     return QPoint ((int)((float)(m_selRect.width()-m_latestCursorPos.x())*m_zoom),
0436                    (int)((float)(m_selRect.height()-m_latestCursorPos.y())*m_zoom));
0437   else if (m_rotation == 270)
0438     return QPoint ((int)((float)m_latestCursorPos.y()*m_zoom),
0439                    (int)((float)(m_selRect.width()-m_latestCursorPos.x())*m_zoom));
0440   else
0441     return QPoint ((int)((float)m_latestCursorPos.x()*m_zoom),
0442                    (int)((float)m_latestCursorPos.y()*m_zoom));
0443 }
0444 
0445 
0446 // MOUSE ACTIONS
0447 
0448 /**
0449  * Called when mouse is clicked inside the window.
0450  *
0451  * @param e
0452  */
0453 void KMagZoomView::mousePressEvent(QMouseEvent *e)
0454 {
0455   switch(e->button()) {
0456   case Qt::LeftButton :
0457     if(m_ctrlKeyPressed) {
0458       // check if currently in resize mode
0459       // don't do anything if fitToWindow is enabled
0460       if ((m_mouseMode != ResizeSelection) && !m_fitToWindow) {
0461         // set the mode to ResizeSelection
0462         m_mouseMode = ResizeSelection;
0463 
0464         // set mouse cursor to "resize all direction"
0465         setCursor(Qt::SizeAllCursor);
0466 
0467         // backup the old position
0468         m_oldMousePos.setX(qRound(e->globalPosition().x()));
0469         m_oldMousePos.setY(qRound(e->globalPosition().y()));
0470 
0471         // set the cursor position to the bottom-right of the selected region
0472         QCursor::setPos(m_selRect.bottomRight());
0473 
0474         // show the selection rectangle
0475         m_selRect.show();
0476       }
0477       else {
0478         // ignore this button press.. so it goes to the parent
0479         e->ignore();
0480       }
0481     } else if(m_shiftKeyPressed) {
0482       // check if currently in move mode
0483       // don't do anything if follow mouse is enabled
0484       if ((m_mouseMode != MoveSelection) && !m_followMouse) {
0485         m_mouseMode = MoveSelection;
0486 
0487         // set mouse cursor to cross hair
0488         setCursor(Qt::CrossCursor);
0489 
0490         // backup the old position
0491         m_oldMousePos.setX(qRound(e->globalPosition().x()));
0492         m_oldMousePos.setY(qRound(e->globalPosition().y()));
0493 
0494         // set the cursor position to the center of the selected region
0495         QCursor::setPos(m_selRect.center());
0496 
0497         // show the selected rectangle
0498         m_selRect.show();
0499       }
0500       else {
0501         // ignore this button press.. so it goes to the parent
0502         e->ignore();
0503       }
0504     } else {
0505       // check if currently in move mode
0506       // don't do anything if follow mouse is enabled
0507       if ((m_mouseMode != GrabSelection) && !m_followMouse) {
0508         m_mouseMode = GrabSelection;
0509 
0510         // set mouse cursor to hand
0511         setCursor(Qt::PointingHandCursor);
0512 
0513         // store the old position
0514         m_oldMousePos.setX(qRound(e->globalPosition().x()));
0515         m_oldMousePos.setY(qRound(e->globalPosition().y()));
0516 
0517         m_oldCenter = m_selRect.center();
0518 
0519         // show the selected rectangle
0520         m_selRect.show();
0521       }
0522       else {
0523         // ignore this button press.. so it goes to the parent
0524         e->ignore();
0525       }
0526     }
0527     break;
0528 
0529   case Qt::MiddleButton :
0530     // check if currently in move mode
0531     // don't do anything if follow mouse is enabled
0532     if ((m_mouseMode != MoveSelection) && !m_followMouse) {
0533       m_mouseMode = MoveSelection;
0534 
0535       // set mouse cursor to cross hair
0536       setCursor(Qt::CrossCursor);
0537 
0538       // backup the old position
0539         m_oldMousePos.setX(qRound(e->globalPosition().x()));
0540         m_oldMousePos.setY(qRound(e->globalPosition().y()));
0541 
0542       // set the cursor position to the center of the selected region
0543       QCursor::setPos(m_selRect.center());
0544 
0545       // show the selected rectangle
0546       m_selRect.show();
0547     }
0548     else {
0549       // ignore this button press.. so it goes to the parent
0550       e->ignore();
0551     }
0552     break;
0553   // do nothing
0554   default:
0555     // ignore this button press.. so it goes to the parent
0556     e->ignore();
0557     break;
0558   }
0559 }
0560 
0561 
0562 /**
0563  * Called when a mouse button is released
0564  *
0565  * @param e
0566  */
0567 void KMagZoomView::mouseReleaseEvent(QMouseEvent *e)
0568 {
0569   switch(e->button()) {
0570   case Qt::LeftButton :
0571   case Qt::MiddleButton :
0572     // check if currently in move mode
0573     if(m_mouseMode == MoveSelection) {
0574       // hide the selection window
0575       m_selRect.hide();
0576       // set the mouse mode to normal
0577       m_mouseMode = Normal;
0578 
0579       // restore the cursor shape
0580       setCursor(Qt::ArrowCursor);
0581 
0582       // restore the cursor position
0583       QCursor::setPos(m_oldMousePos);
0584     } else if(m_mouseMode == ResizeSelection) {
0585       // hide the selection window
0586       m_selRect.hide();
0587       // set the mouse mode to normal
0588       m_mouseMode = Normal;
0589 
0590       // restore the cursor shape
0591       setCursor(Qt::ArrowCursor);
0592 
0593       // restore the cursor position
0594       QCursor::setPos(m_oldMousePos);
0595     } else if(m_mouseMode == GrabSelection) {
0596       // hide the selection window
0597       m_selRect.hide();
0598 
0599       // set the mouse mode to normal
0600       m_mouseMode = Normal;
0601 
0602       // restore the cursor shape
0603       setCursor(Qt::ArrowCursor);
0604     }
0605     break;
0606 
0607   case Qt::RightButton :
0608     break;
0609   case Qt::NoButton :
0610     break;
0611 
0612   // do nothing
0613   default:
0614     ;
0615   }
0616 }
0617 
0618 
0619 /**
0620  * Called when mouse is moved inside the window
0621  *
0622  * @param e
0623  */
0624 void KMagZoomView::mouseMoveEvent(QMouseEvent *e)
0625 {
0626   const QSize screenSize = screen()->virtualSize();
0627   if(m_mouseMode == ResizeSelection) {
0628     // In resize selection mode
0629     // set the current mouse position as the bottom, right corner
0630     m_selRect.setRight(qRound(e->globalPosition().x()));
0631     m_selRect.setBottom(qRound(e->globalPosition().y()));
0632 
0633     m_selRect.update();
0634     grabFrame();
0635   } else if(m_mouseMode == MoveSelection) {
0636      QPoint newCenter;
0637 
0638     // set new center to be the current mouse position
0639     newCenter = e->globalPosition().toPoint();
0640     // make sure the mouse position is not taking the grab window outside
0641     // the display
0642     if(newCenter.x() < m_selRect.width()/2) {
0643       // set X to the minimum possible X
0644       newCenter.setX(m_selRect.width()/2);
0645     } else if(newCenter.x() >=  screenSize.width()-m_selRect.width()/2) {
0646       // set X to the maximum possible X
0647       newCenter.setX(screenSize.width()-m_selRect.width()/2-1);
0648     }
0649 
0650     if(newCenter.y() < m_selRect.height()/2) {
0651       // set Y to the minimum possible Y
0652       newCenter.setY(m_selRect.height()/2);
0653     } else if(newCenter.y() >=  screenSize.height()-m_selRect.height()/2) {
0654       // set Y to the maximum possible Y
0655       newCenter.setY(screenSize.height()-m_selRect.height()/2-1);
0656     }
0657     // move to the new center
0658     m_selRect.moveCenter(newCenter);
0659     // update the grab rectangle display
0660     m_selRect.update();
0661     grabFrame();
0662   } else if(m_mouseMode == GrabSelection) {
0663      QPoint newPos;
0664 
0665     // get new position
0666      newPos = e->globalPosition().toPoint();
0667 
0668     QPoint delta = (newPos - m_oldMousePos)/m_zoom;
0669     QPoint newCenter = m_oldCenter-delta;
0670 
0671     // make sure the mouse position is not taking the grab window outside
0672     // the display
0673     if(newCenter.x() < m_selRect.width()/2) {
0674       // set X to the minimum possible X
0675       newCenter.setX(m_selRect.width()/2);
0676     } else if(newCenter.x() >=  screenSize.width()-m_selRect.width()/2) {
0677       // set X to the maximum possible X
0678       newCenter.setX(screenSize.width()-m_selRect.width()/2-1);
0679     }
0680 
0681     if(newCenter.y() < m_selRect.height()/2) {
0682       // set Y to the minimum possible Y
0683       newCenter.setY(m_selRect.height()/2);
0684     } else if(newCenter.y() >=  screenSize.height()-m_selRect.height()/2) {
0685       // set Y to the maximum possible Y
0686       newCenter.setY(screenSize.height()-m_selRect.height()/2-1);
0687     }
0688 
0689     // move to the new center
0690     m_selRect.moveCenter(newCenter);
0691     // update the grab rectangle display
0692     m_selRect.update();
0693     grabFrame();
0694   }
0695 }
0696 
0697 void KMagZoomView::keyPressEvent(QKeyEvent *e)
0698 {
0699   int offset = 16;
0700   if (e->modifiers() & Qt::ShiftModifier)
0701     offset = 1;
0702 
0703   if (e->key() == Qt::Key_Control)
0704     m_ctrlKeyPressed = true;
0705   else if (e->key() == Qt::Key_Shift)
0706     m_shiftKeyPressed = true;
0707   else if (e->key() == Qt::Key_Left)
0708   {
0709     if (e->modifiers() & Qt::ControlModifier)
0710     {
0711       if (offset >= m_selRect.width())
0712         m_selRect.setWidth (1);
0713       else
0714         m_selRect.setWidth (m_selRect.width()-offset);
0715   }
0716     else if (contentsX() > 0)
0717     {
0718       offset = (int)(offset*m_zoom);
0719       if (contentsX() > offset)
0720         setContentsPos (contentsX()-offset, contentsY());
0721       else
0722         setContentsPos (0, contentsY());
0723     }
0724     else if (m_followMouse == false)
0725     {
0726       if (offset > m_selRect.x())
0727         m_selRect.setX (0);
0728       else
0729         m_selRect.translate (-offset,0);
0730     }
0731     m_selRect.update();
0732   }
0733   else if (e->key() == Qt::Key_Right)
0734   {
0735     if (e->modifiers() & Qt::ControlModifier)
0736     {
0737       const QSize screenSize = screen()->virtualSize();
0738       if (m_selRect.right()+offset >= screenSize.width())
0739         m_selRect.setRight (screenSize.width()-1);
0740       else
0741         m_selRect.setRight (m_selRect.right()+offset);
0742     }
0743     else if (contentsX() < contentsWidth()-visibleWidth())
0744     {
0745       offset = (int)(offset*m_zoom);
0746       if (contentsX()+offset < contentsWidth()-visibleWidth())
0747         setContentsPos (contentsX()+offset, contentsY());
0748       else
0749         setContentsPos (contentsWidth()-visibleWidth(), contentsY());
0750     }
0751     else if (m_followMouse == false)
0752     {
0753       const QSize screenSize = screen()->virtualSize();
0754       if (m_selRect.right()+offset >= screenSize.width())
0755         m_selRect.moveTopRight (QPoint (screenSize.width()-1, m_selRect.top()));
0756       else
0757         m_selRect.translate (offset,0);
0758     }
0759     m_selRect.update();
0760   }
0761   else if (e->key() == Qt::Key_Up)
0762   {
0763     if (e->modifiers() & Qt::ControlModifier)
0764     {
0765       if (offset >= m_selRect.height())
0766         m_selRect.setHeight (1);
0767       else
0768         m_selRect.setHeight (m_selRect.height()-offset);
0769     }
0770     else if (contentsY() > 0)
0771     {
0772       offset = (int)(offset*m_zoom);
0773       if (contentsY() > offset)
0774         setContentsPos (contentsX(), contentsY()-offset);
0775       else
0776         setContentsPos (contentsX(), 0);
0777     }
0778     else if (m_followMouse == false)
0779     {
0780       if (offset > m_selRect.y())
0781         m_selRect.setY (0);
0782       else
0783         m_selRect.translate (0, -offset);
0784     }
0785     m_selRect.update();
0786   }
0787   else if (e->key() == Qt::Key_Down)
0788   {
0789     if (e->modifiers() & Qt::ControlModifier)
0790     {
0791       const QSize screenSize = screen()->virtualSize();
0792       if (m_selRect.bottom()+offset >= screenSize.height())
0793         m_selRect.setBottom (screenSize.height()-1);
0794       else
0795         m_selRect.setBottom (m_selRect.bottom()+offset);
0796     }
0797     else if (contentsY() < contentsHeight()-visibleHeight())
0798     {
0799       offset = (int)(offset*m_zoom);
0800       if (contentsY()+offset < contentsHeight()-visibleHeight())
0801         setContentsPos (contentsX(), contentsY()+offset);
0802       else
0803         setContentsPos (contentsX(), contentsHeight()-visibleHeight());
0804     }
0805     else if (m_followMouse == false)
0806     {
0807       const QSize screenSize = screen()->virtualSize();
0808       if (m_selRect.bottom()+offset >= screenSize.height())
0809         m_selRect.moveBottomLeft (QPoint (m_selRect.left(), screenSize.height()-1));
0810       else
0811         m_selRect.translate (0, offset);
0812     }
0813     m_selRect.update();
0814   }
0815   else
0816     e->ignore();
0817 }
0818 
0819 void KMagZoomView::keyReleaseEvent(QKeyEvent *e)
0820 {
0821   if (e->key() == Qt::Key_Control)
0822     m_ctrlKeyPressed = false;
0823   else if (e->key() == Qt::Key_Shift)
0824     m_shiftKeyPressed = false;
0825   else
0826     e->ignore();
0827 }
0828 
0829 void KMagZoomView::focusOutEvent(QFocusEvent *e)
0830 {
0831   if(e->lostFocus() == true) {
0832     m_ctrlKeyPressed = false;
0833     m_shiftKeyPressed = false;
0834   }
0835 }
0836 
0837 // SLOTS
0838 
0839 /**
0840  * This will fit the zoom view to the view window, thus using the maximum
0841  * possible space in the window.
0842  */
0843 void KMagZoomView::fitToWindow()
0844 {
0845   unsigned int newWidth, newHeight;
0846 
0847   // this is a temporary solution, cast, maybe newWidth and newHeight should be float
0848   if ((m_rotation == 90) || (m_rotation == 270))
0849   {
0850     newWidth = static_cast<unsigned int>((visibleHeight() + m_zoom - 1) / m_zoom);
0851     newHeight = static_cast<unsigned int>((visibleWidth() + m_zoom - 1) / m_zoom);
0852   } else {
0853     newWidth = static_cast<unsigned int>((visibleWidth() + m_zoom - 1) / m_zoom);
0854     newHeight = static_cast<unsigned int>((visibleHeight() + m_zoom - 1) / m_zoom);
0855   }
0856 
0857   QPoint currCenter = m_selRect.center();
0858 
0859   m_selRect.setWidth(newWidth);
0860   m_selRect.setHeight(newHeight);
0861   const QSize screenSize = screen()->virtualSize();
0862    // make sure the selection window does not go outside of the display
0863    if(currCenter.x() < m_selRect.width()/2) {
0864      // set X to the minimum possible X
0865      currCenter.setX(m_selRect.width()/2);
0866    } else if(currCenter.x() >=  screenSize.width()-m_selRect.width()/2) {
0867      // set X to the maximum possible X
0868      currCenter.setX(screenSize.width()-m_selRect.width()/2-1);
0869    }
0870 
0871    if(currCenter.y() < m_selRect.height()/2) {
0872      // set Y to the minimum possible Y
0873      currCenter.setY(m_selRect.height()/2);
0874    } else if(currCenter.y() >=  screenSize.height()-m_selRect.height()/2) {
0875      // set Y to the maximum possible Y
0876      currCenter.setY(screenSize.height()-m_selRect.height()/2-1);
0877    }
0878 
0879   m_selRect.moveCenter(currCenter);
0880   // update the grab rectangle display
0881   m_selRect.update();
0882 //  m_fitToWindow = true;
0883   viewport()->update();
0884 }
0885 
0886 void KMagZoomView::setFitToWindow(bool fit)
0887 {
0888   m_fitToWindow = fit;
0889   if (fit)
0890     fitToWindow();
0891 }
0892 
0893 
0894 /**
0895  * Grabs frame from X
0896  */
0897 void KMagZoomView::grabFrame()
0898 {
0899   // check refresh status
0900   if (!m_refreshSwitch)
0901      return;
0902 
0903   // check if follow-mouse or follow-focus are enabled
0904   if((m_followMouse || m_followFocus) && (m_mouseMode != ResizeSelection)) {
0905     // center-position of the grab-area
0906     QPoint newCenter;
0907 
0908     if(m_followMouse) {
0909         // set new center to be the current mouse position
0910         newCenter = QCursor::pos();
0911 #if HAVE_QACCESSIBILITYCLIENT
0912     } else if(m_followFocus) {
0913         // set the new center to the current keyboard cursor position
0914         newCenter = m_oldFocus;
0915         if(m_followBoth) {
0916             m_followFocus=false;
0917             m_followMouse=true;
0918         }
0919 #endif
0920     }
0921 
0922     // make sure the mouse position is not taking the grab window outside
0923     // the display
0924     const QSize screenSize = screen()->virtualSize();
0925     if(newCenter.x() < m_selRect.width()/2) {
0926       // set X to the minimum possible X
0927       newCenter.setX(m_selRect.width()/2);
0928     } else if(newCenter.x() >=  screenSize.width()-m_selRect.width()/2) {
0929       // set X to the maximum possible X
0930       newCenter.setX(screenSize.width()-m_selRect.width()/2-1);
0931     }
0932 
0933     if(newCenter.y() < m_selRect.height()/2) {
0934       // set Y to the minimum possible Y
0935       newCenter.setY(m_selRect.height()/2);
0936     } else if(newCenter.y() >=  screenSize.height()-m_selRect.height()/2) {
0937       // set Y to the maximum possible Y
0938       newCenter.setY(screenSize.height()-m_selRect.height()/2-1);
0939     }
0940     // move to the new center
0941     m_selRect.moveCenter(newCenter);
0942 
0943     // update the grab rectangle display
0944     m_selRect.update();
0945   }
0946 
0947   // define a normalized rectangle
0948   QRect selRect = m_selRect.normalized();
0949 
0950   auto cursorPos = QCursor::pos();
0951   {
0952     // The cursor position (QPoint) for some reason may be equal to the width or height of the screen,
0953     // for example (1920, foo) or (bar, 1080) for Full HD. At the same time, the geometry rectangle (QRect)
0954     // due to the 0-based coordinate system will not actually contain such point -- the QRect::contains()
0955     // method will always return false, which is incorrect in our case. Therefore, we forcibly "project"
0956     // the coordinates of the cursor position into the screen geometry rectangle.
0957     auto vg = qApp->primaryScreen()->virtualGeometry();
0958     cursorPos.rx() = qBound(vg.left(), cursorPos.x(), vg.right());
0959     cursorPos.ry() = qBound(vg.top(), cursorPos.y(), vg.bottom());
0960   }
0961 
0962   auto screen = qApp->primaryScreen();
0963   for (auto s : qApp->screens()) {
0964     if (s->geometry().contains(cursorPos)) {
0965       screen = s;
0966       break;
0967     }
0968   }
0969 
0970   // grab screenshot from the screen and put it in the pixmap
0971   m_coloredPixmap = screen->grabWindow(
0972     0,  // WId == 0 is interpreted as the root window / desktop
0973     selRect.x() - screen->geometry().left(),
0974     selRect.y() - screen->geometry().top(),
0975     selRect.width(), selRect.height());
0976 
0977   // colorize the grabbed pixmap
0978   if (m_colormode != 0)
0979     m_coloredPixmap = QPixmap::fromImage(ColorSim::recolor(m_coloredPixmap.toImage(), m_colormode));
0980 
0981   // erase background covered by kmag view ...
0982   QRect viewRect = rect();
0983   viewRect.translate(mapTo(window(), QPoint(0, 0)));
0984   viewRect.translate(-selRect.topLeft());
0985   viewRect.translate(window()->geometry().topLeft());
0986   QRegion region(viewRect);
0987 
0988   // ... but exclude own popups ...
0989   const QList<QWidget *> siblings = QApplication::topLevelWidgets();
0990   for (QWidget *sibling : siblings) {
0991     if (sibling != window() && (sibling->windowType() & Qt::Window) && sibling->isVisible()) {
0992       QRect rect = sibling->frameGeometry();
0993       rect.translate(-selRect.topLeft());
0994       region -= rect;
0995     }
0996   }
0997 
0998   QPainter p(&m_coloredPixmap);
0999   for (const QRect &rect : region) {
1000     p.fillRect(rect, palette().dark());
1001   }
1002   p.end();
1003 
1004   QRect r = m_zoomMatrix.mapRect(m_coloredPixmap.rect());
1005   // call repaint to display the newly grabbed image
1006   horizontalScrollBar()->setPageStep(r.width());
1007   verticalScrollBar()->setPageStep(r.height());
1008   viewport()->update();
1009 }
1010 
1011 
1012 /**
1013  * Updates the mouse cursor in the zoom view.
1014  */
1015 void KMagZoomView::updateMouseView()
1016 {
1017     if (m_fps < 8)
1018         viewport()->update();
1019 }
1020 
1021 /**
1022  * Toggles the state of refreshing.
1023  */
1024 void KMagZoomView::toggleRefresh()
1025 {
1026   if(m_refreshSwitch) {
1027     m_refreshSwitch = false;
1028     m_grabTimer.stop();
1029     m_mouseViewTimer.stop();
1030   } else {
1031     m_refreshSwitch = true;
1032     m_grabTimer.start(1000/m_fps);
1033     m_mouseViewTimer.start(40);
1034   }
1035 }
1036 
1037 /**
1038  * This function sets the zoom value to be used.
1039  */
1040 void KMagZoomView::setZoom(float zoom)
1041 {
1042   // use this zoom
1043   m_zoom = zoom;
1044 
1045   // update selection window size when zooming in if necessary
1046   if (m_fitToWindow)
1047     fitToWindow();
1048 
1049   // recompute the zoom matrix
1050   setupMatrix();
1051 
1052   viewport()->update();
1053 }
1054 
1055 /**
1056  * This function sets the rotation value to be used.
1057  */
1058 void KMagZoomView::setRotation(int rotation)
1059 {
1060   // use this rotation
1061   m_rotation = rotation;
1062 
1063   // update selection window size if necessary
1064   if (m_fitToWindow)
1065     fitToWindow();
1066 
1067   // recompute the zoom matrix
1068   setupMatrix();
1069 
1070   viewport()->update();
1071 }
1072 
1073 /**
1074  * Set a new color simulation mode.
1075  */
1076 void KMagZoomView::setColorMode(int mode)
1077 {
1078   if (m_colormode != mode) {
1079     m_colormode = mode;
1080     viewport()->update();
1081   }
1082 }
1083 
1084 /**
1085  * Set a new refresh rate.
1086  */
1087 void KMagZoomView::setRefreshRate(float fps)
1088 {
1089   if(fps < 0.1)
1090     return;
1091   m_fps = static_cast<unsigned int>(fps);
1092 
1093   if(m_grabTimer.isActive())
1094     m_grabTimer.start(static_cast<int>(1000.0/m_fps));
1095 }
1096 
1097 void KMagZoomView::showSelRect(bool show)
1098 {
1099   m_selRect.alwaysVisible(show);
1100   if(show) {
1101     m_selRect.show();
1102   } else if(m_mouseMode == Normal) {
1103     m_selRect.hide();
1104   }
1105 }
1106 
1107 /**
1108  * Sets the selection rectangle to the given position.
1109  */
1110 void KMagZoomView::setSelRectPos(const QRect & rect)
1111 {
1112   m_selRect.setRect(rect.x(), rect.y(), rect.width(), rect.height());
1113   m_selRect.update();
1114   grabFrame();
1115 }
1116 
1117 bool KMagZoomView::showMouse(unsigned int type)
1118 {
1119   if(int(type) > m_showMouseTypes.count()-1)
1120     return (false);
1121   else
1122     m_showMouse = type;
1123 
1124   return(true);
1125 }
1126 
1127 unsigned int KMagZoomView::getShowMouseType() const
1128 {
1129   return (m_showMouse);
1130 }
1131 
1132 QStringList KMagZoomView::getShowMouseStringList() const
1133 {
1134   return (m_showMouseTypes);
1135 }
1136 
1137 
1138 /**
1139  * Returns the image which is being displayed. It's again drawn by adding
1140  * the mouse cursor if needed.
1141  */
1142 QImage KMagZoomView::getImage()
1143 {
1144   QImage image = m_coloredPixmap.transformed(m_zoomMatrix).toImage();
1145 
1146   // show the pixel under mouse cursor
1147   if(m_showMouse && !image.isNull()) {
1148     // paint the mouse cursor w/o updating to a newer position
1149     paintMouseCursor(&image, calcMousePos(false));
1150   }
1151   return(image);
1152 }
1153 
1154 #include "moc_kmagzoomview.cpp"