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