File indexing completed on 2023-10-01 05:57:57
0001 /** 0002 * SPDX-FileCopyrightText: (C) 2007 Luca Gugelmann <lucag@student.ethz.ch> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "regiongrabber.h" 0008 0009 #include <QApplication> 0010 #include <QDesktopWidget> 0011 #include <QLocale> 0012 #include <QToolTip> 0013 #include <QtGui/QMouseEvent> 0014 #include <QtGui/QPainter> 0015 #include <QScreen> 0016 0017 #include <KLocalizedString> 0018 #include <KWindowSystem> 0019 0020 RegionGrabber::RegionGrabber() 0021 : QWidget(nullptr) 0022 , selection() 0023 , mouseDown(false) 0024 , newSelection(false) 0025 , handleSize(10) 0026 , mouseOverHandle(nullptr) 0027 , idleTimer() 0028 , showHelp(true) 0029 , grabbing(false) 0030 , TLHandle(0, 0, handleSize, handleSize) 0031 , TRHandle(0, 0, handleSize, handleSize) 0032 , BLHandle(0, 0, handleSize, handleSize) 0033 , BRHandle(0, 0, handleSize, handleSize) 0034 , LHandle(0, 0, handleSize, handleSize) 0035 , THandle(0, 0, handleSize, handleSize) 0036 , RHandle(0, 0, handleSize, handleSize) 0037 , BHandle(0, 0, handleSize, handleSize) 0038 { 0039 handles << &TLHandle << &TRHandle << &BLHandle << &BRHandle << &LHandle << &THandle << &RHandle << &BHandle; 0040 setMouseTracking(true); 0041 setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); 0042 int timeout = KWindowSystem::compositingActive() ? 200 : 50; 0043 QTimer::singleShot(timeout, this, SLOT(init())); 0044 connect(&idleTimer, &QTimer::timeout, this, &RegionGrabber::displayHelp); 0045 idleTimer.start(3000); 0046 } 0047 0048 RegionGrabber::~RegionGrabber() 0049 { 0050 } 0051 0052 void RegionGrabber::init() 0053 { 0054 pixmap = QApplication::primaryScreen()->grabWindow(QApplication::desktop()->winId()); 0055 showFullScreen(); // krazy:exclude=qmethods -- Necessary for proper screenshot capture. 0056 resize(pixmap.size()); 0057 move(0, 0); 0058 setCursor(Qt::CrossCursor); 0059 grabKeyboard(); 0060 } 0061 0062 void RegionGrabber::displayHelp() 0063 { 0064 showHelp = true; 0065 update(); 0066 } 0067 0068 void RegionGrabber::paintEvent(QPaintEvent *e) 0069 { 0070 Q_UNUSED(e); 0071 if (grabbing) // grabWindow() should just get the background 0072 return; 0073 0074 QPainter painter(this); 0075 0076 QPalette pal(QToolTip::palette()); 0077 QFont font = QToolTip::font(); 0078 0079 QColor handleColor = pal.color(QPalette::Active, QPalette::Highlight); 0080 handleColor.setAlpha(160); 0081 QColor overlayColor(0, 0, 0, 160); 0082 QColor textColor = pal.color(QPalette::Active, QPalette::Text); 0083 QColor textBackgroundColor = pal.color(QPalette::Active, QPalette::Base); 0084 painter.drawPixmap(0, 0, pixmap); 0085 painter.setFont(font); 0086 0087 QRect r = selection.normalized().adjusted(0, 0, -1, -1); 0088 if (!selection.isNull()) { 0089 QRegion grey(rect()); 0090 grey = grey.subtracted(r); 0091 painter.setPen(handleColor); 0092 painter.setBrush(overlayColor); 0093 painter.setClipRegion(grey); 0094 painter.drawRect(-1, -1, rect().width() + 1, rect().height() + 1); 0095 painter.setClipRect(rect()); 0096 painter.setBrush(Qt::NoBrush); 0097 painter.drawRect(r); 0098 } 0099 0100 if (showHelp) { 0101 painter.setPen(textColor); 0102 painter.setBrush(textBackgroundColor); 0103 QString helpText = i18n("Select a region using the mouse. To take the snapshot, press the Enter key. Press Esc to quit."); 0104 QRect textRect = painter.boundingRect(rect().adjusted(2, 2, -2, -2), Qt::TextWordWrap, helpText); 0105 textRect.adjust(-2, -2, 4, 2); 0106 painter.drawRect(textRect); 0107 textRect.moveTopLeft(QPoint(3, 3)); 0108 painter.drawText(textRect, helpText); 0109 } 0110 0111 if (selection.isNull()) { 0112 return; 0113 } 0114 0115 // The grabbed region is everything which is covered by the drawn 0116 // rectangles (border included). This means that there is no 0px 0117 // selection, since a 0px wide rectangle will always be drawn as a line. 0118 QString txt = QString("%1x%2").arg(selection.width() == 0 ? 2 : selection.width()).arg(selection.height() == 0 ? 2 : selection.height()); 0119 QRect textRect = painter.boundingRect(rect(), Qt::AlignLeft, txt); 0120 QRect boundingRect = textRect.adjusted(-4, 0, 0, 0); 0121 0122 if (textRect.width() < r.width() - 2 * handleSize && textRect.height() < r.height() - 2 * handleSize && (r.width() > 100 && r.height() > 100)) { // center, unsuitable for small selections 0123 boundingRect.moveCenter(r.center()); 0124 textRect.moveCenter(r.center()); 0125 } else if (r.y() - 3 > textRect.height() && r.x() + textRect.width() < rect().right()) { // on top, left aligned 0126 boundingRect.moveBottomLeft(QPoint(r.x(), r.y() - 3)); 0127 textRect.moveBottomLeft(QPoint(r.x() + 2, r.y() - 3)); 0128 } else if (r.x() - 3 > textRect.width()) { // left, top aligned 0129 boundingRect.moveTopRight(QPoint(r.x() - 3, r.y())); 0130 textRect.moveTopRight(QPoint(r.x() - 5, r.y())); 0131 } else if (r.bottom() + 3 + textRect.height() < rect().bottom() && r.right() > textRect.width()) { // at bottom, right aligned 0132 boundingRect.moveTopRight(QPoint(r.right(), r.bottom() + 3)); 0133 textRect.moveTopRight(QPoint(r.right() - 2, r.bottom() + 3)); 0134 } else if (r.right() + textRect.width() + 3 < rect().width()) { // right, bottom aligned 0135 boundingRect.moveBottomLeft(QPoint(r.right() + 3, r.bottom())); 0136 textRect.moveBottomLeft(QPoint(r.right() + 5, r.bottom())); 0137 } 0138 // if the above didn't catch it, you are running on a very tiny screen... 0139 painter.setPen(textColor); 0140 painter.setBrush(textBackgroundColor); 0141 painter.drawRect(boundingRect); 0142 painter.drawText(textRect, txt); 0143 0144 if ((r.height() > handleSize * 2 && r.width() > handleSize * 2) || !mouseDown) { 0145 updateHandles(); 0146 painter.setPen(handleColor); 0147 handleColor.setAlpha(60); 0148 painter.setBrush(handleColor); 0149 painter.drawRects(handleMask().begin(),handleMask().rectCount()); 0150 } 0151 } 0152 0153 void RegionGrabber::resizeEvent(QResizeEvent *e) 0154 { 0155 Q_UNUSED(e); 0156 if (selection.isNull()) 0157 return; 0158 QRect r = selection; 0159 r.setTopLeft(limitPointToRect(r.topLeft(), rect())); 0160 r.setBottomRight(limitPointToRect(r.bottomRight(), rect())); 0161 if (r.width() <= 1 || r.height() <= 1) // this just results in ugly drawing... 0162 r = QRect(); 0163 selection = r; 0164 } 0165 0166 void RegionGrabber::mousePressEvent(QMouseEvent *e) 0167 { 0168 showHelp = false; 0169 idleTimer.stop(); 0170 if (e->button() == Qt::LeftButton) { 0171 mouseDown = true; 0172 dragStartPoint = e->pos(); 0173 selectionBeforeDrag = selection; 0174 if (!selection.contains(e->pos())) { 0175 newSelection = true; 0176 selection = QRect(); 0177 showHelp = true; 0178 } else { 0179 setCursor(Qt::ClosedHandCursor); 0180 } 0181 } else if (e->button() == Qt::RightButton) { 0182 newSelection = false; 0183 selection = QRect(); 0184 setCursor(Qt::CrossCursor); 0185 } 0186 update(); 0187 } 0188 0189 void RegionGrabber::mouseMoveEvent(QMouseEvent *e) 0190 { 0191 if (mouseDown) { 0192 if (newSelection) { 0193 QPoint p = e->pos(); 0194 QRect r = rect(); 0195 selection = QRect(dragStartPoint, limitPointToRect(p, r)).normalized(); 0196 } else if (mouseOverHandle == nullptr) { // moving the whole selection 0197 QRect r = rect().normalized(), s = selectionBeforeDrag.normalized(); 0198 QPoint p = s.topLeft() + e->pos() - dragStartPoint; 0199 r.setBottomRight(r.bottomRight() - QPoint(s.width(), s.height())); 0200 if (!r.isNull() && r.isValid()) 0201 selection.moveTo(limitPointToRect(p, r)); 0202 } else { // dragging a handle 0203 QRect r = selectionBeforeDrag; 0204 QPoint offset = e->pos() - dragStartPoint; 0205 0206 if (mouseOverHandle == &TLHandle || mouseOverHandle == &THandle || mouseOverHandle == &TRHandle) { // dragging one of the top handles 0207 r.setTop(r.top() + offset.y()); 0208 } 0209 0210 if (mouseOverHandle == &TLHandle || mouseOverHandle == &LHandle || mouseOverHandle == &BLHandle) { // dragging one of the left handles 0211 r.setLeft(r.left() + offset.x()); 0212 } 0213 0214 if (mouseOverHandle == &BLHandle || mouseOverHandle == &BHandle || mouseOverHandle == &BRHandle) { // dragging one of the bottom handles 0215 r.setBottom(r.bottom() + offset.y()); 0216 } 0217 0218 if (mouseOverHandle == &TRHandle || mouseOverHandle == &RHandle || mouseOverHandle == &BRHandle) { // dragging one of the right handles 0219 r.setRight(r.right() + offset.x()); 0220 } 0221 r = r.normalized(); 0222 r.setTopLeft(limitPointToRect(r.topLeft(), rect())); 0223 r.setBottomRight(limitPointToRect(r.bottomRight(), rect())); 0224 selection = r; 0225 } 0226 update(); 0227 } else { 0228 if (selection.isNull()) 0229 return; 0230 bool found = false; 0231 Q_FOREACH (QRect *r, handles) { 0232 if (r->contains(e->pos())) { 0233 mouseOverHandle = r; 0234 found = true; 0235 break; 0236 } 0237 } 0238 if (!found) { 0239 mouseOverHandle = nullptr; 0240 if (selection.contains(e->pos())) 0241 setCursor(Qt::OpenHandCursor); 0242 else 0243 setCursor(Qt::CrossCursor); 0244 } else { 0245 if (mouseOverHandle == &TLHandle || mouseOverHandle == &BRHandle) 0246 setCursor(Qt::SizeFDiagCursor); 0247 if (mouseOverHandle == &TRHandle || mouseOverHandle == &BLHandle) 0248 setCursor(Qt::SizeBDiagCursor); 0249 if (mouseOverHandle == &LHandle || mouseOverHandle == &RHandle) 0250 setCursor(Qt::SizeHorCursor); 0251 if (mouseOverHandle == &THandle || mouseOverHandle == &BHandle) 0252 setCursor(Qt::SizeVerCursor); 0253 } 0254 } 0255 } 0256 0257 void RegionGrabber::mouseReleaseEvent(QMouseEvent *e) 0258 { 0259 mouseDown = false; 0260 newSelection = false; 0261 idleTimer.start(); 0262 if (mouseOverHandle == nullptr && selection.contains(e->pos())) 0263 setCursor(Qt::OpenHandCursor); 0264 update(); 0265 } 0266 0267 void RegionGrabber::mouseDoubleClickEvent(QMouseEvent *) 0268 { 0269 grabRect(); 0270 } 0271 0272 void RegionGrabber::keyPressEvent(QKeyEvent *e) 0273 { 0274 if (e->key() == Qt::Key_Escape) { 0275 Q_EMIT regionGrabbed(QPixmap()); 0276 } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { 0277 grabRect(); 0278 } else { 0279 e->ignore(); 0280 } 0281 } 0282 0283 void RegionGrabber::grabRect() 0284 { 0285 QRect r = selection.normalized(); 0286 if (!r.isNull() && r.isValid()) { 0287 grabbing = true; 0288 Q_EMIT regionGrabbed(pixmap.copy(r)); 0289 } 0290 } 0291 0292 void RegionGrabber::updateHandles() 0293 { 0294 QRect r = selection.normalized().adjusted(0, 0, -1, -1); 0295 int s2 = handleSize / 2; 0296 0297 TLHandle.moveTopLeft(r.topLeft()); 0298 TRHandle.moveTopRight(r.topRight()); 0299 BLHandle.moveBottomLeft(r.bottomLeft()); 0300 BRHandle.moveBottomRight(r.bottomRight()); 0301 0302 LHandle.moveTopLeft(QPoint(r.x(), r.y() + r.height() / 2 - s2)); 0303 THandle.moveTopLeft(QPoint(r.x() + r.width() / 2 - s2, r.y())); 0304 RHandle.moveTopRight(QPoint(r.right(), r.y() + r.height() / 2 - s2)); 0305 BHandle.moveBottomLeft(QPoint(r.x() + r.width() / 2 - s2, r.bottom())); 0306 } 0307 0308 QRegion RegionGrabber::handleMask() const 0309 { 0310 // note: not normalized QRects are bad here, since they will not be drawn 0311 QRegion mask; 0312 Q_FOREACH (QRect *rect, handles) 0313 mask += QRegion(*rect); 0314 return mask; 0315 } 0316 0317 QPoint RegionGrabber::limitPointToRect(const QPoint &p, const QRect &r) const 0318 { 0319 QPoint q; 0320 q.setX(p.x() < r.x() ? r.x() : p.x() < r.right() ? p.x() : r.right()); 0321 q.setY(p.y() < r.y() ? r.y() : p.y() < r.bottom() ? p.y() : r.bottom()); 0322 return q; 0323 } 0324 0325 #include "moc_regiongrabber.cpp"