File indexing completed on 2024-04-28 07:27:20
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"