You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
384 lines
12 KiB
384 lines
12 KiB
/***************************************************************************
|
|
* Copyright (C) 2009 - 2013 by Artem 'DOOMer' Galichkin *
|
|
* doomer3d@gmail.com *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
***************************************************************************/
|
|
|
|
#include "src/core/regionselect.h"
|
|
|
|
#include <QDesktopWidget>
|
|
#include <QApplication>
|
|
#include <QScreen>
|
|
|
|
RegionSelect::RegionSelect(Config *mainconf, QWidget *parent)
|
|
:QWidget(parent)
|
|
{
|
|
_conf = mainconf;
|
|
sharedInit();
|
|
|
|
move(0, 0);
|
|
drawBackGround();
|
|
|
|
_processSelection = false;
|
|
_fittedSelection = false;
|
|
|
|
show();
|
|
|
|
grabKeyboard();
|
|
grabMouse();
|
|
}
|
|
|
|
RegionSelect::RegionSelect(Config* mainconf, const QRect& lastRect, QWidget* parent)
|
|
: QWidget(parent)
|
|
{
|
|
_conf = mainconf;
|
|
sharedInit();
|
|
_selectRect = lastRect;
|
|
|
|
move(0, 0);
|
|
drawBackGround();
|
|
|
|
_processSelection = false;
|
|
_fittedSelection = false;
|
|
|
|
show();
|
|
|
|
grabKeyboard();
|
|
grabMouse();
|
|
}
|
|
|
|
RegionSelect::~RegionSelect()
|
|
{
|
|
_conf = NULL;
|
|
delete _conf;
|
|
}
|
|
|
|
void RegionSelect::sharedInit()
|
|
{
|
|
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
|
|
setWindowState(Qt::WindowFullScreen);
|
|
setCursor(Qt::CrossCursor);
|
|
|
|
_sizeDesktop = QApplication::desktop()->size();
|
|
resize(_sizeDesktop);
|
|
|
|
const QList<QScreen *> screens = qApp->screens();
|
|
const QDesktopWidget *desktop = QApplication::desktop();
|
|
const int screenNum = desktop->screenNumber(QCursor::pos());
|
|
|
|
if (screenNum < screens.count()) {
|
|
_desktopPixmapBkg = screens[screenNum]->grabWindow(desktop->winId());
|
|
}
|
|
|
|
_desktopPixmapClr = _desktopPixmapBkg;
|
|
}
|
|
|
|
void RegionSelect::paintEvent(QPaintEvent *event)
|
|
{
|
|
Q_UNUSED(event)
|
|
QPainter painter(this);
|
|
|
|
painter.drawPixmap(QPoint(0, 0), _desktopPixmapBkg);
|
|
|
|
drawRectSelection(painter);
|
|
}
|
|
|
|
void RegionSelect::mousePressEvent(QMouseEvent* event)
|
|
{
|
|
if (event->button() != Qt::LeftButton && event->button() != Qt::RightButton)
|
|
return;
|
|
|
|
_selStartPoint = event->pos();
|
|
_processSelection = true;
|
|
}
|
|
|
|
void RegionSelect::mouseReleaseEvent(QMouseEvent* event)
|
|
{
|
|
_selEndPoint = event->pos();
|
|
_processSelection = false;
|
|
if (event->button() == Qt::RightButton && !_fittedSelection)
|
|
selectFit();
|
|
}
|
|
|
|
void RegionSelect::mouseDoubleClickEvent(QMouseEvent* event)
|
|
{
|
|
if (event->button() != Qt::LeftButton)
|
|
return;
|
|
|
|
Q_EMIT processDone(true);
|
|
}
|
|
|
|
void RegionSelect::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
if (_processSelection)
|
|
{
|
|
_selEndPoint = event->pos();
|
|
_selectRect = QRect(_selStartPoint, _selEndPoint).normalized();
|
|
_fittedSelection = false;
|
|
update();
|
|
}
|
|
}
|
|
|
|
void RegionSelect::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
// canceled select screen area
|
|
if (event->key() == Qt::Key_Escape)
|
|
Q_EMIT processDone(false);
|
|
// canceled select screen area
|
|
else if (event->key() == Qt::Key_Space)
|
|
selectFit();
|
|
else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)
|
|
Q_EMIT processDone(true);
|
|
else
|
|
event->ignore();
|
|
}
|
|
|
|
void RegionSelect::drawBackGround()
|
|
{
|
|
// create painter on pixelmap of desktop
|
|
QPainter painter(&_desktopPixmapBkg);
|
|
|
|
// set painter brush on 85% transparency
|
|
painter.setBrush(QBrush(QColor(0, 0, 0, 85), Qt::SolidPattern));
|
|
|
|
// draw rect of desktop size in poainter
|
|
painter.drawRect(QApplication::desktop()->rect());
|
|
|
|
QRect txtRect = QApplication::desktop()->screenGeometry(QApplication::desktop()->primaryScreen());
|
|
QString txtTip = QApplication::tr("Use your mouse to draw a rectangle to screenshot or exit pressing\nany key or using the right or middle mouse buttons.");
|
|
|
|
txtRect.setHeight(qRound((float) (txtRect.height() / 10))); // rounded val of text rect height
|
|
|
|
painter.setPen(QPen(Qt::red)); // ste message rect border color
|
|
painter.setBrush(QBrush(QColor(255, 255, 255, 180), Qt::SolidPattern));
|
|
QRect txtBgRect = painter.boundingRect(txtRect, Qt::AlignCenter, txtTip);
|
|
|
|
// set height & width of bkg rect
|
|
txtBgRect.setX(txtBgRect.x() - 6);
|
|
txtBgRect.setY(txtBgRect.y() - 4);
|
|
txtBgRect.setWidth(txtBgRect.width() + 12);
|
|
txtBgRect.setHeight(txtBgRect.height() + 8);
|
|
|
|
painter.drawRect(txtBgRect);
|
|
|
|
// Draw the text
|
|
painter.setPen(QPen(Qt::black)); // black color pen
|
|
painter.drawText(txtBgRect, Qt::AlignCenter, txtTip);
|
|
|
|
// set bkg to pallette widget
|
|
QPalette newPalette = palette();
|
|
newPalette.setBrush(QPalette::Window, QBrush(_desktopPixmapBkg));
|
|
setPalette(newPalette);
|
|
}
|
|
|
|
void RegionSelect::drawRectSelection(QPainter &painter)
|
|
{
|
|
painter.drawPixmap(_selectRect, _desktopPixmapClr, _selectRect);
|
|
painter.setPen(QPen(QBrush(QColor(0, 0, 0, 255)), 2));
|
|
painter.drawRect(_selectRect);
|
|
|
|
QString txtSize = QApplication::tr("%1 x %2 pixels ").arg(_selectRect.width()).arg(_selectRect.height());
|
|
painter.drawText(_selectRect, Qt::AlignBottom | Qt::AlignRight, txtSize);
|
|
|
|
if (!_selEndPoint.isNull() && _conf->getZoomAroundMouse())
|
|
{
|
|
const quint8 zoomSide = 200;
|
|
|
|
// create magnifer coords
|
|
QPoint zoomStart = _selEndPoint;
|
|
zoomStart -= QPoint(zoomSide/5, zoomSide/5); // 40, 40
|
|
|
|
QPoint zoomEnd = _selEndPoint;
|
|
zoomEnd += QPoint(zoomSide/5, zoomSide/5);
|
|
|
|
// creating rect area for magnifer
|
|
QRect zoomRect = QRect(zoomStart, zoomEnd);
|
|
|
|
QPixmap zoomPixmap = _desktopPixmapClr.copy(zoomRect).scaled(QSize(zoomSide, zoomSide), Qt::KeepAspectRatio);
|
|
|
|
QPainter zoomPainer(&zoomPixmap); // create painter from pixmap maignifer
|
|
zoomPainer.setPen(QPen(QBrush(QColor(255, 0, 0, 180)), 2));
|
|
zoomPainer.drawRect(zoomPixmap.rect()); // draw
|
|
zoomPainer.drawText(zoomPixmap.rect().center() - QPoint(4, -4), "+");
|
|
|
|
// position for drawing preview
|
|
QPoint zoomCenter = _selectRect.bottomRight();
|
|
|
|
if (zoomCenter.x() + zoomSide > _desktopPixmapClr.rect().width()
|
|
|| zoomCenter.y() + zoomSide > _desktopPixmapClr.rect().height())
|
|
zoomCenter -= QPoint(zoomSide, zoomSide);
|
|
painter.drawPixmap(zoomCenter, zoomPixmap);
|
|
}
|
|
}
|
|
|
|
void RegionSelect::selectFit()
|
|
{
|
|
if (_fittedSelection)
|
|
_currentFit = (_currentFit + 1) % _fitRectangles.size();
|
|
else
|
|
{
|
|
findFit();
|
|
_currentFit = 1;
|
|
_fittedSelection = true;
|
|
}
|
|
_selectRect = _fitRectangles[_currentFit];
|
|
update();
|
|
}
|
|
|
|
void RegionSelect::findFit()
|
|
{
|
|
QRect boundingRect;
|
|
int left = _selectRect.left();
|
|
int top = _selectRect.top();
|
|
int right = _selectRect.right();
|
|
int bottom = _selectRect.bottom();
|
|
|
|
_fitRectangles.clear();
|
|
_fitRectangles.push_back(_selectRect);
|
|
|
|
// Set the rectangle in which to search for borders
|
|
if (_conf->getFitInside())
|
|
boundingRect = _selectRect;
|
|
else
|
|
{
|
|
boundingRect.setLeft(qMax(left - fitRectExpand, 0));
|
|
boundingRect.setTop(qMax(top - fitRectExpand, 0));
|
|
boundingRect.setRight(qMin(right + fitRectExpand, _sizeDesktop.width() - 1));
|
|
boundingRect.setBottom(qMin(bottom + fitRectExpand, _sizeDesktop.height() - 1));
|
|
}
|
|
|
|
// Find borders inside boundingRect
|
|
fitBorder(boundingRect, LEFT, left);
|
|
fitBorder(boundingRect, TOP, top);
|
|
fitBorder(boundingRect, RIGHT, right);
|
|
fitBorder(boundingRect, BOTTOM, bottom);
|
|
|
|
const QRect fitRectangle = QRect(QPoint(left, top), QPoint(right, bottom));
|
|
_fitRectangles.push_back(fitRectangle);
|
|
}
|
|
|
|
void RegionSelect::fitBorder(const QRect &boundRect, enum Side side, int &border)
|
|
{
|
|
const QImage boundImage = _desktopPixmapClr.copy(boundRect).toImage();
|
|
|
|
// Set the relative coordinates of a box vertex and a vector along the box side
|
|
QPoint startPoint;
|
|
QPoint directionVector;
|
|
switch(side){
|
|
case TOP:
|
|
startPoint = QPoint(0,0);
|
|
directionVector = QPoint(1,0);
|
|
break;
|
|
case RIGHT:
|
|
startPoint = boundRect.topRight() - boundRect.topLeft();
|
|
directionVector = QPoint(0,1);
|
|
break;
|
|
case BOTTOM:
|
|
startPoint = boundRect.bottomRight() - boundRect.topLeft();
|
|
directionVector = QPoint(-1,0);
|
|
break;
|
|
case LEFT:
|
|
startPoint = boundRect.bottomLeft() - boundRect.topLeft();
|
|
directionVector = QPoint(0,-1);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
// Set vector normal to the box side
|
|
QPoint normalVector = QPoint(-directionVector.y(), directionVector.x());
|
|
|
|
// Setbox dimensions relative to the box side
|
|
int directionLength;
|
|
int normalDepth;
|
|
if (directionVector.x() == 0)
|
|
{
|
|
directionLength = boundRect.height() - 1;
|
|
normalDepth = boundRect.width() - 1;
|
|
}
|
|
else
|
|
{
|
|
directionLength = boundRect.width() - 1;
|
|
normalDepth = boundRect.height() - 1;
|
|
}
|
|
|
|
// Set how deep in the boundingRect to search for the border
|
|
if (_conf->getFitInside())
|
|
normalDepth = qMin(normalDepth/2 - 1, fitRectDepth);
|
|
else
|
|
normalDepth = qMin(normalDepth/2 - 1, fitRectDepth + fitRectExpand);
|
|
|
|
QVector<int> gradient = QVector<int>(normalDepth, 0);
|
|
QVector<int> preR = QVector<int>(normalDepth + 1, 0);
|
|
QVector<int> preG = QVector<int>(normalDepth + 1, 0);
|
|
QVector<int> preB = QVector<int>(normalDepth + 1, 0);
|
|
|
|
// Compute pixel Sobel normal gradients and add their absolute values parallel to the box side
|
|
for (int i = 1; i < directionLength; i++)
|
|
{
|
|
for (int j = 0; j <= normalDepth; j++)
|
|
{
|
|
QPoint point = startPoint + i*directionVector + j*normalVector;
|
|
QRgb pixelL = boundImage.pixel(point - directionVector);
|
|
QRgb pixelC = boundImage.pixel(point);
|
|
QRgb pixelR = boundImage.pixel(point + directionVector);
|
|
preR[j] = qRed(pixelL) + 2*qRed(pixelC) + qRed(pixelR);
|
|
preG[j] = qGreen(pixelL) + 2*qGreen(pixelC) + qGreen(pixelR);
|
|
preB[j] = qBlue(pixelL) + 2*qBlue(pixelC) + qBlue(pixelR);
|
|
}
|
|
for (int j = 1; j < normalDepth; j++)
|
|
gradient[j] += qAbs(preR[j-1] - preR[j+1]) + qAbs(preG[j-1] - preG[j+1]) + qAbs(preB[j-1] - preB[j+1]);
|
|
}
|
|
|
|
int maxGradient = 0;
|
|
int positionMax = 0;
|
|
for (int j = 1; j < normalDepth; j++)
|
|
{
|
|
// Scale pixel normal gradients and break if drop detected
|
|
gradient[j] = 1 + gradient[j]/(60*directionLength);
|
|
if (_conf->getFitInside() && gradient[j] <= maxGradient/2)
|
|
break;
|
|
|
|
// Keep searching for the maximum normal gradient
|
|
if (gradient[j] > maxGradient)
|
|
{
|
|
maxGradient = gradient[j];
|
|
positionMax = j;
|
|
}
|
|
}
|
|
// If all normal gradients small, keep the original user selected border
|
|
if (maxGradient <= 1)
|
|
return;
|
|
|
|
// Transform computed border back to original coordinates
|
|
startPoint = boundRect.topLeft() + startPoint + (positionMax + 1)*normalVector;
|
|
if (normalVector.x() == 0)
|
|
border = startPoint.y();
|
|
else
|
|
border = startPoint.x();
|
|
}
|
|
|
|
QPixmap RegionSelect::getSelection()
|
|
{
|
|
QPixmap sel;
|
|
sel = _desktopPixmapClr.copy(_selectRect);
|
|
return sel;
|
|
}
|
|
|
|
QPoint RegionSelect::getSelectionStartPos()
|
|
{
|
|
return _selectRect.topLeft();
|
|
}
|