diff --git a/config-oiio.h.cmake b/config-oiio.h.cmake
deleted file mode 100644
index ac4ea698e7..0000000000
--- a/config-oiio.h.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-/* config-ocio.h. Generated by cmake from config-ocio.h.cmake */
-
-/* Define if you have ocio, the OpenColorIO Library */
-#cmakedefine HAVE_OCIO 1
-
-
-
diff --git a/krita/data/symbols/CMakeLists.txt b/krita/data/symbols/CMakeLists.txt
index 483d7399e5..889d695435 100644
--- a/krita/data/symbols/CMakeLists.txt
+++ b/krita/data/symbols/CMakeLists.txt
@@ -1,6 +1,7 @@
install( FILES
BalloonSymbols.svg
pepper_carrot_speech_bubbles.svg
+ preset_icons.svg
DESTINATION ${DATA_INSTALL_DIR}/krita/symbols)
diff --git a/krita/data/symbols/preset_icons.svg b/krita/data/symbols/preset_icons.svg
new file mode 100644
index 0000000000..1c29478ddb
--- /dev/null
+++ b/krita/data/symbols/preset_icons.svg
@@ -0,0 +1,2281 @@
+
+
diff --git a/libs/ui/KisNodeDelegate.cpp b/libs/ui/KisNodeDelegate.cpp
index 19fae61fb7..49b8838d6e 100644
--- a/libs/ui/KisNodeDelegate.cpp
+++ b/libs/ui/KisNodeDelegate.cpp
@@ -1,883 +1,895 @@
/*
Copyright (c) 2006 Gábor Lehel
Copyright (c) 2008 Cyrille Berger
Copyright (c) 2011 José Luis Vergara
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kis_config.h"
#include "KisNodeDelegate.h"
#include "kis_node_model.h"
#include "KisNodeToolTip.h"
#include "KisNodeView.h"
#include "KisPart.h"
#include "input/kis_input_manager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_node_view_color_scheme.h"
#include "kis_icon_utils.h"
#include "kis_layer_properties_icons.h"
#include "krita_utils.h"
+#include "kis_config_notifier.h"
typedef KisBaseNode::Property* OptionalProperty;
#include
class KisNodeDelegate::Private
{
public:
Private() : view(0), edit(0) { }
KisNodeView *view;
QPointer edit;
KisNodeToolTip tip;
+ QColor checkersColor1;
+ QColor checkersColor2;
+
QList rightmostProperties(const KisBaseNode::PropertyList &props) const;
int numProperties(const QModelIndex &index) const;
OptionalProperty findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const;
OptionalProperty findVisibilityProperty(KisBaseNode::PropertyList &props) const;
void toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty prop, bool controlPressed, const QModelIndex &index);
};
KisNodeDelegate::KisNodeDelegate(KisNodeView *view, QObject *parent)
: QAbstractItemDelegate(parent)
, d(new Private)
{
d->view = view;
QApplication::instance()->installEventFilter(this);
+ connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
+ slotConfigChanged();
}
KisNodeDelegate::~KisNodeDelegate()
{
delete d;
}
QSize KisNodeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
return QSize(option.rect.width(), scm.rowHeight());
}
void KisNodeDelegate::paint(QPainter *p, const QStyleOptionViewItem &o, const QModelIndex &index) const
{
p->save();
{
QStyleOptionViewItem option = getOptions(o, index);
QStyle *style = option.widget ? option.widget->style() : QApplication::style();
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget);
bool shouldGrayOut = index.data(KisNodeModel::ShouldGrayOutRole).toBool();
if (shouldGrayOut) {
option.state &= ~QStyle::State_Enabled;
}
p->setFont(option.font);
drawColorLabel(p, option, index);
drawFrame(p, option, index);
drawThumbnail(p, option, index);
drawText(p, option, index);
drawIcons(p, option, index);
drawVisibilityIconHijack(p, option, index);
drawDecoration(p, option, index);
drawExpandButton(p, option, index);
drawBranch(p, option, index);
drawProgressBar(p, option, index);
}
p->restore();
}
void KisNodeDelegate::drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const {
Q_UNUSED(index);
KisNodeViewColorScheme scm;
const QPoint base = scm.relThumbnailRect().translated(option.rect.topLeft()).topLeft() - QPoint( scm.indentation(), 0);
// there is no indention if we are starting negative, so don't draw a branch
if (base.x() < 0) {
return;
}
QPen oldPen = p->pen();
const qreal oldOpacity = p->opacity(); // remember previous opacity
p->setOpacity(1.0);
QColor color = scm.gridColor(option, d->view);
QColor bgColor = option.state & QStyle::State_Selected ?
qApp->palette().color(QPalette::Base) :
qApp->palette().color(QPalette::Text);
color = KritaUtils::blendColors(color, bgColor, 0.9);
// TODO: if we are a mask type, use dotted lines for the branch style
// p->setPen(QPen(p->pen().color(), 2, Qt::DashLine, Qt::RoundCap, Qt::RoundJoin));
p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
QPoint p2 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize()*0.45);
QPoint p3 = base + QPoint(scm.iconSize() - scm.decorationMargin()*2, scm.iconSize());
QPoint p4 = base + QPoint(scm.iconSize()*1.4, scm.iconSize());
p->drawLine(p2, p3);
p->drawLine(p3, p4);
// draw parent lines (keep drawing until x position is less than 0
QPoint p5 = p2 - QPoint(scm.indentation(), 0);
QPoint p6 = p3 - QPoint(scm.indentation(), 0);
QPoint parentBase1 = p5;
QPoint parentBase2 = p6;
// indent lines needs to be very subtle to avoid making the docker busy looking
color = KritaUtils::blendColors(color, bgColor, 0.9); // makes it a little lighter than L lines
p->setPen(QPen(color, 0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
while (parentBase1.x() > scm.visibilityColumnWidth()) {
p->drawLine(parentBase1, parentBase2);
parentBase1 = parentBase1 - QPoint(scm.indentation(), 0);
parentBase2 = parentBase2 - QPoint(scm.indentation(), 0);
}
p->setPen(oldPen);
p->setOpacity(oldOpacity);
}
void KisNodeDelegate::drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const int label = index.data(KisNodeModel::ColorLabelIndexRole).toInt();
QColor color = scm.colorLabel(label);
if (color.alpha() <= 0) return;
QColor bgColor = qApp->palette().color(QPalette::Base);
color = KritaUtils::blendColors(color, bgColor, 0.3);
const QRect rect = option.state & QStyle::State_Selected ?
iconsRect(option, index) :
option.rect.adjusted(-scm.indentation(), 0, 0, 0);
p->fillRect(rect, color);
}
void KisNodeDelegate::drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
QPen oldPen = p->pen();
p->setPen(scm.gridColor(option, d->view));
const QPoint base = option.rect.topLeft();
QPoint p2 = base + QPoint(-scm.indentation() - 1, 0);
QPoint p3 = base + QPoint(2 * scm.decorationMargin() + scm.decorationSize(), 0);
QPoint p4 = base + QPoint(-1, 0);
QPoint p5(iconsRect(option,
index).left() - 1, base.y());
QPoint p6(option.rect.right(), base.y());
QPoint v(0, option.rect.height());
// draw a line that goes the length of the entire frame. one for the
// top, and one for the bottom
QPoint pTopLeft(0, option.rect.topLeft().y());
QPoint pTopRight(option.rect.bottomRight().x(),option.rect.topLeft().y() );
p->drawLine(pTopLeft, pTopRight);
QPoint pBottomLeft(0, option.rect.topLeft().y() + scm.rowHeight());
QPoint pBottomRight(option.rect.bottomRight().x(),option.rect.topLeft().y() + scm.rowHeight() );
p->drawLine(pBottomLeft, pBottomRight);
const bool paintForParent =
index.parent().isValid() &&
!index.row();
if (paintForParent) {
QPoint p1(-2 * scm.indentation() - 1, 0);
p1 += base;
p->drawLine(p1, p2);
}
QPoint k0(0, base.y());
QPoint k1(1 * scm.border() + 2 * scm.visibilityMargin() + scm.visibilitySize(), base.y());
p->drawLine(k0, k1);
p->drawLine(k0 + v, k1 + v);
p->drawLine(k0, k0 + v);
p->drawLine(k1, k1 + v);
p->drawLine(p2, p6);
p->drawLine(p2 + v, p6 + v);
p->drawLine(p2, p2 + v);
p->drawLine(p3, p3 + v);
p->drawLine(p4, p4 + v);
p->drawLine(p5, p5 + v);
p->drawLine(p6, p6 + v);
//// For debugging purposes only
//p->setPen(Qt::blue);
//KritaUtils::renderExactRect(p, iconsRect(option, index));
//KritaUtils::renderExactRect(p, textRect(option, index));
//KritaUtils::renderExactRect(p, scm.relThumbnailRect().translated(option.rect.topLeft()));
p->setPen(oldPen);
}
void KisNodeDelegate::drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const int thumbSize = scm.thumbnailSize();
const qreal oldOpacity = p->opacity(); // remember previous opacity
QImage img = index.data(int(KisNodeModel::BeginThumbnailRole) + thumbSize).value();
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.35);
}
QRect fitRect = scm.relThumbnailRect().translated(option.rect.topLeft());
QPoint offset;
offset.setX((fitRect.width() - img.width()) / 2);
offset.setY((fitRect.height() - img.height()) / 2);
offset += fitRect.topLeft();
- KisConfig cfg;
-
// paint in a checkerboard pattern behind the layer contents to represent transparent
const int step = scm.thumbnailSize() / 6;
QImage checkers(2 * step, 2 * step, QImage::Format_ARGB32);
QPainter gc(&checkers);
- gc.fillRect(QRect(0, 0, step, step), cfg.checkersColor1());
- gc.fillRect(QRect(step, 0, step, step), cfg.checkersColor2());
- gc.fillRect(QRect(step, step, step, step), cfg.checkersColor1());
- gc.fillRect(QRect(0, step, step, step), cfg.checkersColor2());
+ gc.fillRect(QRect(0, 0, step, step), d->checkersColor1);
+ gc.fillRect(QRect(step, 0, step, step), d->checkersColor2);
+ gc.fillRect(QRect(step, step, step, step), d->checkersColor1);
+ gc.fillRect(QRect(0, step, step, step), d->checkersColor2);
QBrush brush(checkers);
p->setBrushOrigin(offset);
p->fillRect(img.rect().translated(offset), brush);
p->drawImage(offset, img);
p->setOpacity(oldOpacity); // restore old opacity
QRect borderRect = kisGrowRect(img.rect(), 1).translated(offset);
KritaUtils::renderExactRect(p, borderRect, scm.gridColor(option, d->view));
}
QRect KisNodeDelegate::iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
int propCount = d->numProperties(index);
const int iconsWidth =
propCount * (scm.iconSize() + 2 * scm.iconMargin()) +
(propCount - 1) * scm.border();
const int x = option.rect.x() + option.rect.width()
- (iconsWidth + scm.border());
const int y = option.rect.y() + scm.border();
return QRect(x, y,
iconsWidth,
scm.rowHeight() - scm.border());
}
QRect KisNodeDelegate::textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
static QFont f;
static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely
if (minbearing == 2003 || f != option.font) {
f = option.font; //getting your bearings can be expensive, so we cache them
minbearing = option.fontMetrics.minLeftBearing() + option.fontMetrics.minRightBearing();
}
const int decorationOffset =
2 * scm.border() +
2 * scm.decorationMargin() +
scm.decorationSize();
const int width =
iconsRect(option, index).left() - option.rect.x() -
scm.border() + minbearing - decorationOffset;
return QRect(option.rect.x() - minbearing + decorationOffset,
option.rect.y() + scm.border(),
width,
scm.rowHeight() - scm.border());
}
void KisNodeDelegate::drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const QRect rc = textRect(option, index)
.adjusted(scm.textMargin(), 0, -scm.textMargin(), 0);
QPen oldPen = p->pen();
const qreal oldOpacity = p->opacity(); // remember previous opacity
p->setPen(option.palette.color(QPalette::Active,QPalette::Text ));
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.55);
}
const QString text = index.data(Qt::DisplayRole).toString();
const QString elided = elidedText(p->fontMetrics(), rc.width(), Qt::ElideRight, text);
p->drawText(rc, Qt::AlignLeft | Qt::AlignVCenter, elided);
p->setPen(oldPen); // restore pen settings
p->setOpacity(oldOpacity);
}
QList KisNodeDelegate::Private::rightmostProperties(const KisBaseNode::PropertyList &props) const
{
QList list;
QList prependList;
list << OptionalProperty(0);
list << OptionalProperty(0);
list << OptionalProperty(0);
KisBaseNode::PropertyList::const_iterator it = props.constBegin();
KisBaseNode::PropertyList::const_iterator end = props.constEnd();
for (; it != end; ++it) {
if (!it->isMutable) continue;
if (it->id == KisLayerPropertiesIcons::visible.id()) {
// noop...
} else if (it->id == KisLayerPropertiesIcons::locked.id()) {
list[0] = OptionalProperty(&(*it));
} else if (it->id == KisLayerPropertiesIcons::inheritAlpha.id()) {
list[1] = OptionalProperty(&(*it));
} else if (it->id == KisLayerPropertiesIcons::alphaLocked.id()) {
list[2] = OptionalProperty(&(*it));
} else {
prependList.prepend(OptionalProperty(&(*it)));
}
}
{
QMutableListIterator i(prependList);
i.toBack();
while (i.hasPrevious()) {
OptionalProperty val = i.previous();
int emptyIndex = list.lastIndexOf(0);
if (emptyIndex < 0) break;
list[emptyIndex] = val;
i.remove();
}
}
return prependList + list;
}
int KisNodeDelegate::Private::numProperties(const QModelIndex &index) const
{
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
QList realProps = rightmostProperties(props);
return realProps.size();
}
OptionalProperty KisNodeDelegate::Private::findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const
{
KisBaseNode::PropertyList::iterator it = props.begin();
KisBaseNode::PropertyList::iterator end = props.end();
for (; it != end; ++it) {
if (it->id == refProp->id) {
return &(*it);
}
}
return 0;
}
OptionalProperty KisNodeDelegate::Private::findVisibilityProperty(KisBaseNode::PropertyList &props) const
{
KisBaseNode::PropertyList::iterator it = props.begin();
KisBaseNode::PropertyList::iterator end = props.end();
for (; it != end; ++it) {
if (it->id == KisLayerPropertiesIcons::visible.id()) {
return &(*it);
}
}
return 0;
}
void KisNodeDelegate::drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
const QRect r = iconsRect(option, index);
QTransform oldTransform = p->transform();
QPen oldPen = p->pen();
p->setTransform(QTransform::fromTranslate(r.x(), r.y()));
p->setPen(scm.gridColor(option, d->view));
int x = 0;
const int y = (scm.rowHeight() - scm.border() - scm.iconSize()) / 2;
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
QList realProps = d->rightmostProperties(props);
Q_FOREACH (OptionalProperty prop, realProps) {
x += scm.iconMargin();
if (prop) {
QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon;
bool fullColor = prop->state.toBool() && option.state & QStyle::State_Enabled;
const qreal oldOpacity = p->opacity(); // remember previous opacity
if (fullColor) {
p->setOpacity(1.0);
}
else {
p->setOpacity(0.35);
}
p->drawPixmap(x, y, icon.pixmap(scm.iconSize(), QIcon::Normal));
p->setOpacity(oldOpacity); // restore old opacity
}
x += scm.iconSize() + scm.iconMargin();
p->drawLine(x, 0, x, scm.rowHeight() - scm.border());
x += scm.border();
}
p->setTransform(oldTransform);
p->setPen(oldPen);
}
QRect KisNodeDelegate::visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
return QRect(scm.border(), scm.border() + option.rect.top(),
2 * scm.visibilityMargin() + scm.visibilitySize(),
scm.rowHeight() - scm.border());
}
QRect KisNodeDelegate::decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option);
Q_UNUSED(index);
KisNodeViewColorScheme scm;
QRect realVisualRect = d->view->originalVisualRect(index);
return QRect(realVisualRect.left(), scm.border() + realVisualRect.top(),
2 * scm.decorationMargin() + scm.decorationSize(),
scm.rowHeight() - scm.border());
}
void KisNodeDelegate::drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
/**
* Small hack Alert:
*
* Here wepaint over the area that sits basically outside our layer's
* row. Anyway, just update it later...
*/
KisNodeViewColorScheme scm;
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
OptionalProperty prop = d->findVisibilityProperty(props);
if (!prop) return;
const int x = scm.border() + scm.visibilityMargin();
const int y = option.rect.top() + (scm.rowHeight() - scm.border() - scm.visibilitySize()) / 2;
QIcon icon = prop->state.toBool() ? prop->onIcon : prop->offIcon;
p->setOpacity(1.0);
p->drawPixmap(x, y, icon.pixmap(scm.visibilitySize(), QIcon::Normal));
//// For debugging purposes only
// p->save();
// p->setPen(Qt::blue);
// KritaUtils::renderExactRect(p, visibilityClickRect(option, index));
// p->restore();
}
void KisNodeDelegate::drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
KisNodeViewColorScheme scm;
QIcon icon = index.data(Qt::DecorationRole).value();
if (!icon.isNull()) {
QPixmap pixmap =
icon.pixmap(scm.decorationSize(),
(option.state & QStyle::State_Enabled) ?
QIcon::Normal : QIcon::Disabled);
const QRect rc = scm.relDecorationRect().translated(option.rect.topLeft());
const qreal oldOpacity = p->opacity(); // remember previous opacity
if (!(option.state & QStyle::State_Enabled)) {
p->setOpacity(0.35);
}
p->drawPixmap(rc.topLeft(), pixmap);
p->setOpacity(oldOpacity); // restore old opacity
}
}
void KisNodeDelegate::drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
KisNodeViewColorScheme scm;
QRect rc = scm.relExpandButtonRect().translated(option.rect.topLeft());
rc = kisGrowRect(rc, 0);
if (!(option.state & QStyle::State_Children)) {
return;
}
QString iconName = option.state & QStyle::State_Open ? "arrow-down" : "arrow-right";
QIcon icon = KisIconUtils::loadIcon(iconName);
QPixmap pixmap = icon.pixmap(rc.width(),
(option.state & QStyle::State_Enabled) ?
QIcon::Normal : QIcon::Disabled);
p->drawPixmap(rc.topLeft(), pixmap);
}
void KisNodeDelegate::Private::toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty clickedProperty, bool controlPressed, const QModelIndex &index)
{
QAbstractItemModel *model = view->model();
// Using Ctrl+click to enter stasis
if (controlPressed && clickedProperty->canHaveStasis) {
// STEP 0: Prepare to Enter or Leave control key stasis
quint16 numberOfLeaves = model->rowCount(index.parent());
QModelIndex eachItem;
// STEP 1: Go.
if (clickedProperty->isInStasis == false) { // Enter
/* Make every leaf of this node go State = False, saving the old property value to stateInStasis */
for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent())
eachItem = model->index(i, 1, index.parent());
// The entire property list has to be altered because model->setData cannot set individual properties
KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value();
OptionalProperty prop = findProperty(eachPropertyList, clickedProperty);
if (!prop) continue;
prop->stateInStasis = prop->state.toBool();
prop->state = eachItem == index;
prop->isInStasis = true;
model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole);
}
for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent())
eachItem = model->index(i, 1, index.parent());
KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value();
OptionalProperty prop = findProperty(eachPropertyList, clickedProperty);
if (!prop) continue;
}
} else { // Leave
/* Make every leaf of this node go State = stateInStasis */
for (quint16 i = 0; i < numberOfLeaves; ++i) {
eachItem = model->index(i, 1, index.parent());
// The entire property list has to be altered because model->setData cannot set individual properties
KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value();
OptionalProperty prop = findProperty(eachPropertyList, clickedProperty);
if (!prop) continue;
prop->state = prop->stateInStasis;
prop->isInStasis = false;
model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole);
}
}
} else {
clickedProperty->state = !clickedProperty->state.toBool();
model->setData(index, QVariant::fromValue(props), KisNodeModel::PropertiesRole);
}
}
bool KisNodeDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
KisNodeViewColorScheme scm;
if ((event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
&& (index.flags() & Qt::ItemIsEnabled))
{
QMouseEvent *mouseEvent = static_cast(event);
/**
* Small hack Alert:
*
* Here we handle clicking even when it happened outside
* the rectangle of the current index. The point is, we
* use some virtual scroling offset to move the tree to the
* right of the visibility icon. So the icon itself is placed
* in an empty area that doesn't belong to any index. But we still
* handle it.
*/
const QRect iconsRect = this->iconsRect(option, index);
const bool iconsClicked = iconsRect.isValid() &&
iconsRect.contains(mouseEvent->pos());
const QRect visibilityRect = visibilityClickRect(option, index);
const bool visibilityClicked = visibilityRect.isValid() &&
visibilityRect.contains(mouseEvent->pos());
const QRect decorationRect = decorationClickRect(option, index);
const bool decorationClicked = decorationRect.isValid() &&
decorationRect.contains(mouseEvent->pos());
const bool leftButton = mouseEvent->buttons() & Qt::LeftButton;
if (leftButton && iconsClicked) {
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
QList realProps = d->rightmostProperties(props);
const int numProps = realProps.size();
const int iconWidth = scm.iconSize() + 2 * scm.iconMargin() + scm.border();
const int xPos = mouseEvent->pos().x() - iconsRect.left();
const int clickedIcon = xPos / iconWidth;
const int distToBorder = qMin(xPos % iconWidth, iconWidth - xPos % iconWidth);
if (iconsClicked &&
clickedIcon >= 0 &&
clickedIcon < numProps &&
distToBorder > scm.iconMargin()) {
OptionalProperty clickedProperty = realProps[clickedIcon];
if (!clickedProperty) return false;
d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index);
return true;
}
} else if (leftButton && visibilityClicked) {
KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value();
OptionalProperty clickedProperty = d->findVisibilityProperty(props);
if (!clickedProperty) return false;
d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index);
return true;
} else if (leftButton && decorationClicked) {
bool isExpandable = model->hasChildren(index);
if (isExpandable) {
bool isExpanded = d->view->isExpanded(index);
d->view->setExpanded(index, !isExpanded);
return true;
}
}
if (mouseEvent->button() == Qt::LeftButton &&
mouseEvent->modifiers() == Qt::AltModifier) {
d->view->setCurrentIndex(index);
model->setData(index, true, KisNodeModel::AlternateActiveRole);
return true;
}
}
else if (event->type() == QEvent::ToolTip) {
if (!KisConfig().hidePopups()) {
QHelpEvent *helpEvent = static_cast(event);
d->tip.showTip(d->view, helpEvent->pos(), option, index);
}
return true;
} else if (event->type() == QEvent::Leave) {
d->tip.hide();
}
return false;
}
QWidget *KisNodeDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex&) const
{
d->edit = new QLineEdit(parent);
d->edit->installEventFilter(const_cast(this)); //hack?
return d->edit;
}
void KisNodeDelegate::setEditorData(QWidget *widget, const QModelIndex &index) const
{
QLineEdit *edit = qobject_cast(widget);
Q_ASSERT(edit);
edit->setText(index.data(Qt::DisplayRole).toString());
}
void KisNodeDelegate::setModelData(QWidget *widget, QAbstractItemModel *model, const QModelIndex &index) const
{
QLineEdit *edit = qobject_cast(widget);
Q_ASSERT(edit);
model->setData(index, edit->text(), Qt::DisplayRole);
}
void KisNodeDelegate::updateEditorGeometry(QWidget *widget, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(index);
widget->setGeometry(option.rect);
}
// PROTECTED
bool KisNodeDelegate::eventFilter(QObject *object, QEvent *event)
{
switch (event->type()) {
case QEvent::MouseButtonPress: {
if (d->edit) {
QMouseEvent *me = static_cast(event);
if (!QRect(d->edit->mapToGlobal(QPoint()), d->edit->size()).contains(me->globalPos())) {
emit commitData(d->edit);
emit closeEditor(d->edit);
}
}
} break;
case QEvent::KeyPress: {
QLineEdit *edit = qobject_cast(object);
if (edit && edit == d->edit) {
QKeyEvent *ke = static_cast(event);
switch (ke->key()) {
case Qt::Key_Escape:
emit closeEditor(edit);
return true;
case Qt::Key_Tab:
emit commitData(edit);
emit closeEditor(edit,EditNextItem);
return true;
case Qt::Key_Backtab:
emit commitData(edit);
emit closeEditor(edit, EditPreviousItem);
return true;
case Qt::Key_Return:
case Qt::Key_Enter:
emit commitData(edit);
emit closeEditor(edit);
return true;
default: break;
}
}
} break;
case QEvent::ShortcutOverride : {
QLineEdit *edit = qobject_cast(object);
if (edit && edit == d->edit){
auto* key = static_cast(event);
if (key->modifiers() == Qt::NoModifier){
switch (key->key()){
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
case Qt::Key_Return:
case Qt::Key_Enter:
event->accept();
return true;
default: break;
}
}
}
} break;
case QEvent::FocusOut : {
QLineEdit *edit = qobject_cast(object);
if (edit && edit == d->edit) {
emit commitData(edit);
emit closeEditor(edit);
}
}
default: break;
}
return QAbstractItemDelegate::eventFilter(object, event);
}
// PRIVATE
QStyleOptionViewItem KisNodeDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index)
{
QStyleOptionViewItem option = o;
QVariant v = index.data(Qt::FontRole);
if (v.isValid()) {
option.font = v.value();
option.fontMetrics = QFontMetrics(option.font);
}
v = index.data(Qt::TextAlignmentRole);
if (v.isValid())
option.displayAlignment = QFlag(v.toInt());
v = index.data(Qt::TextColorRole);
if (v.isValid())
option.palette.setColor(QPalette::Text, v.value());
v = index.data(Qt::BackgroundColorRole);
if (v.isValid())
option.palette.setColor(QPalette::Window, v.value());
return option;
}
QRect KisNodeDelegate::progressBarRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return iconsRect(option, index);
}
void KisNodeDelegate::drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QVariant value = index.data(KisNodeModel::ProgressRole);
if (!value.isNull() && (value.toInt() >= 0 && value.toInt() <= 100)) {
const QRect r = progressBarRect(option, index);
p->save();
{
p->setClipRect(r);
QStyle* style = QApplication::style();
QStyleOptionProgressBar opt;
opt.minimum = 0;
opt.maximum = 100;
opt.progress = value.toInt();
opt.textVisible = true;
opt.textAlignment = Qt::AlignHCenter;
opt.text = i18n("%1 %", opt.progress);
opt.rect = r;
opt.orientation = Qt::Horizontal;
opt.state = option.state;
style->drawControl(QStyle::CE_ProgressBar, &opt, p, 0);
}
p->restore();
}
}
+
+void KisNodeDelegate::slotConfigChanged()
+{
+ KisConfig cfg;
+
+ d->checkersColor1 = cfg.checkersColor1();
+ d->checkersColor2 = cfg.checkersColor2();
+}
diff --git a/libs/ui/KisNodeDelegate.h b/libs/ui/KisNodeDelegate.h
index 9ccd9a693f..1cd0606c0a 100644
--- a/libs/ui/KisNodeDelegate.h
+++ b/libs/ui/KisNodeDelegate.h
@@ -1,82 +1,85 @@
/*
Copyright (c) 2006 Gábor Lehel
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KIS_DOCUMENT_SECTION_DELEGATE_H
#define KIS_DOCUMENT_SECTION_DELEGATE_H
#include
class KisNodeView;
class KisNodeModel;
/**
* See KisNodeModel and KisNodeView.
*
* A delegate provides the gui machinery, using Qt's model/view terminology.
* This class is owned by KisNodeView to do the work of generating the
* graphical representation of each item.
*/
class KisNodeDelegate: public QAbstractItemDelegate
{
Q_OBJECT
public:
explicit KisNodeDelegate(KisNodeView *view, QObject *parent = 0);
~KisNodeDelegate() override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const override;
protected:
bool eventFilter(QObject *object, QEvent *event) override;
private:
typedef KisNodeModel Model;
typedef KisNodeView View;
class Private;
Private* const d;
static QStyleOptionViewItem getOptions(const QStyleOptionViewItem &option, const QModelIndex &index);
QRect progressBarRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawProgressBar(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawBranch(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawColorLabel(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawFrame(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawThumbnail(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QRect iconsRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
QRect textRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawText(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
QRect visibilityClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
QRect decorationClickRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawDecoration(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void drawExpandButton(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+private Q_SLOTS:
+ void slotConfigChanged();
};
#endif
diff --git a/libs/ui/canvas/kis_coordinates_converter.cpp b/libs/ui/canvas/kis_coordinates_converter.cpp
index b8329a97a6..36f6aa4805 100644
--- a/libs/ui/canvas/kis_coordinates_converter.cpp
+++ b/libs/ui/canvas/kis_coordinates_converter.cpp
@@ -1,441 +1,441 @@
/*
* Copyright (c) 2010 Dmitry Kazakov
* Copyright (c) 2011 Silvio Heinrich
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include
#include "kis_coordinates_converter.h"
#include
#include
#include
#include
struct KisCoordinatesConverter::Private {
Private():
isXAxisMirrored(false), isYAxisMirrored(false), rotationAngle(0.0) { }
KisImageWSP image;
bool isXAxisMirrored;
bool isYAxisMirrored;
qreal rotationAngle;
QSizeF canvasWidgetSize;
QPointF documentOffset;
QTransform flakeToWidget;
QTransform imageToDocument;
QTransform documentToFlake;
QTransform widgetToViewport;
};
/**
* When vastScrolling value is less than 0.5 it is possible
* that the whole scrolling area (viewport) will be smaller than
* the size of the widget. In such cases the image should be
* centered in the widget. Previously we used a special parameter
* documentOrigin for this purpose, now the value for this
* centering is calculated dynamically, helping the offset to
* center the image inside the widget
*
* Note that the correction is null when the size of the document
* plus vast scrolling reserve is larger than the widget. This
* is always true for vastScrolling parameter > 0.5.
*/
QPointF KisCoordinatesConverter::centeringCorrection() const
{
KisConfig cfg;
QSize documentSize = imageRectInWidgetPixels().toAlignedRect().size();
QPointF dPoint(documentSize.width(), documentSize.height());
QPointF wPoint(m_d->canvasWidgetSize.width(), m_d->canvasWidgetSize.height());
QPointF minOffset = -cfg.vastScrolling() * wPoint;
QPointF maxOffset = dPoint - wPoint + cfg.vastScrolling() * wPoint;
QPointF range = maxOffset - minOffset;
range.rx() = qMin(range.x(), (qreal)0.0);
range.ry() = qMin(range.y(), (qreal)0.0);
range /= 2;
return -range;
}
/**
* The document offset and the position of the top left corner of the
* image must always coincide, that is why we need to correct them to
* and fro.
*
* When we change zoom level, the calculation of the new offset is
* done by KoCanvasControllerWidget, that is why we just passively fix
* the flakeToWidget transform to conform the offset and wait until
* the canvas controller will recenter us.
*
* But when we do our own transformations of the canvas, like rotation
* and mirroring, we cannot rely on the centering of the canvas
* controller and we do it ourselves. Then we just set new offset and
* return its value to be set in the canvas controller explicitly.
*/
void KisCoordinatesConverter::correctOffsetToTransformation()
{
m_d->documentOffset = -(imageRectInWidgetPixels().topLeft() -
centeringCorrection()).toPoint();
}
void KisCoordinatesConverter::correctTransformationToOffset()
{
QPointF topLeft = imageRectInWidgetPixels().topLeft();
QPointF diff = (-topLeft) - m_d->documentOffset;
diff += centeringCorrection();
m_d->flakeToWidget *= QTransform::fromTranslate(diff.x(), diff.y());
}
void KisCoordinatesConverter::recalculateTransformations()
{
if(!m_d->image) return;
m_d->imageToDocument = QTransform::fromScale(1 / m_d->image->xRes(),
1 / m_d->image->yRes());
qreal zoomX, zoomY;
KoZoomHandler::zoom(&zoomX, &zoomY);
m_d->documentToFlake = QTransform::fromScale(zoomX, zoomY);
correctTransformationToOffset();
QRectF irect = imageRectInWidgetPixels();
QRectF wrect = QRectF(QPoint(0,0), m_d->canvasWidgetSize);
QRectF rrect = irect & wrect;
QTransform reversedTransform = flakeToWidgetTransform().inverted();
QRectF canvasBounds = reversedTransform.mapRect(rrect);
QPointF offset = canvasBounds.topLeft();
m_d->widgetToViewport = reversedTransform * QTransform::fromTranslate(-offset.x(), -offset.y());
}
KisCoordinatesConverter::KisCoordinatesConverter()
: m_d(new Private) { }
KisCoordinatesConverter::~KisCoordinatesConverter()
{
delete m_d;
}
void KisCoordinatesConverter::setCanvasWidgetSize(QSize size)
{
m_d->canvasWidgetSize = size;
recalculateTransformations();
}
void KisCoordinatesConverter::setImage(KisImageWSP image)
{
m_d->image = image;
recalculateTransformations();
}
void KisCoordinatesConverter::setDocumentOffset(const QPoint& offset)
{
QPointF diff = m_d->documentOffset - offset;
m_d->documentOffset = offset;
m_d->flakeToWidget *= QTransform::fromTranslate(diff.x(), diff.y());
recalculateTransformations();
}
QPoint KisCoordinatesConverter::documentOffset() const
{
return QPoint(int(m_d->documentOffset.x()), int(m_d->documentOffset.y()));
}
qreal KisCoordinatesConverter::rotationAngle() const
{
return m_d->rotationAngle;
}
void KisCoordinatesConverter::setZoom(qreal zoom)
{
KoZoomHandler::setZoom(zoom);
recalculateTransformations();
}
qreal KisCoordinatesConverter::effectiveZoom() const
{
qreal scaleX, scaleY;
this->imageScale(&scaleX, &scaleY);
if (scaleX != scaleY) {
qWarning() << "WARNING: Zoom is not isotropic!" << ppVar(scaleX) << ppVar(scaleY) << ppVar(qFuzzyCompare(scaleX, scaleY));
}
// zoom by average of x and y
return 0.5 * (scaleX + scaleY);
}
QPoint KisCoordinatesConverter::rotate(QPointF center, qreal angle)
{
QTransform rot;
rot.rotate(angle);
m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(),-center.y());
m_d->flakeToWidget *= rot;
m_d->flakeToWidget *= QTransform::fromTranslate(center.x(), center.y());
m_d->rotationAngle = std::fmod(m_d->rotationAngle + angle, 360.0);
correctOffsetToTransformation();
recalculateTransformations();
return m_d->documentOffset.toPoint();
}
QPoint KisCoordinatesConverter::mirror(QPointF center, bool mirrorXAxis, bool mirrorYAxis)
{
bool keepOrientation = false; // XXX: Keep here for now, maybe some day we can restore the parameter again.
bool doXMirroring = m_d->isXAxisMirrored ^ mirrorXAxis;
bool doYMirroring = m_d->isYAxisMirrored ^ mirrorYAxis;
qreal scaleX = doXMirroring ? -1.0 : 1.0;
qreal scaleY = doYMirroring ? -1.0 : 1.0;
QTransform mirror = QTransform::fromScale(scaleX, scaleY);
QTransform rot;
rot.rotate(m_d->rotationAngle);
m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(),-center.y());
if (keepOrientation) {
m_d->flakeToWidget *= rot.inverted();
}
m_d->flakeToWidget *= mirror;
if (keepOrientation) {
m_d->flakeToWidget *= rot;
}
m_d->flakeToWidget *= QTransform::fromTranslate(center.x(),center.y());
if (!keepOrientation && (doXMirroring ^ doYMirroring)) {
m_d->rotationAngle = -m_d->rotationAngle;
}
m_d->isXAxisMirrored = mirrorXAxis;
m_d->isYAxisMirrored = mirrorYAxis;
correctOffsetToTransformation();
recalculateTransformations();
return m_d->documentOffset.toPoint();
}
bool KisCoordinatesConverter::xAxisMirrored() const
{
return m_d->isXAxisMirrored;
}
bool KisCoordinatesConverter::yAxisMirrored() const
{
return m_d->isYAxisMirrored;
}
QPoint KisCoordinatesConverter::resetRotation(QPointF center)
{
QTransform rot;
rot.rotate(-m_d->rotationAngle);
m_d->flakeToWidget *= QTransform::fromTranslate(-center.x(), -center.y());
m_d->flakeToWidget *= rot;
m_d->flakeToWidget *= QTransform::fromTranslate(center.x(), center.y());
m_d->rotationAngle = 0.0;
correctOffsetToTransformation();
recalculateTransformations();
return m_d->documentOffset.toPoint();
}
QTransform KisCoordinatesConverter::imageToWidgetTransform() const{
return m_d->imageToDocument * m_d->documentToFlake * m_d->flakeToWidget;
}
QTransform KisCoordinatesConverter::imageToDocumentTransform() const {
return m_d->imageToDocument;
}
QTransform KisCoordinatesConverter::documentToFlakeTransform() const {
return m_d->documentToFlake;
}
QTransform KisCoordinatesConverter::flakeToWidgetTransform() const {
return m_d->flakeToWidget;
}
QTransform KisCoordinatesConverter::documentToWidgetTransform() const
{
return m_d->documentToFlake * m_d->flakeToWidget;
}
QTransform KisCoordinatesConverter::viewportToWidgetTransform() const {
return m_d->widgetToViewport.inverted();
}
QTransform KisCoordinatesConverter::imageToViewportTransform() const {
return m_d->imageToDocument * m_d->documentToFlake * m_d->flakeToWidget * m_d->widgetToViewport;
}
void KisCoordinatesConverter::getQPainterCheckersInfo(QTransform *transform,
QPointF *brushOrigin,
- QPolygonF *polygon) const
+ QPolygonF *polygon,
+ const bool scrollCheckers) const
{
/**
* Qt has different rounding for QPainter::drawRect/drawImage.
* The image is rounded mathematically, while rect in aligned
* to the next integer. That causes transparent line appear on
* the canvas.
*
* See: https://bugreports.qt.nokia.com/browse/QTBUG-22827
*/
QRectF imageRect = imageRectInViewportPixels();
imageRect.adjust(0,0,-0.5,-0.5);
- KisConfig cfg;
- if (cfg.scrollCheckers()) {
+ if (scrollCheckers) {
*transform = viewportToWidgetTransform();
*polygon = imageRect;
*brushOrigin = imageToViewport(QPointF(0,0));
}
else {
*transform = QTransform();
*polygon = viewportToWidgetTransform().map(imageRect);
*brushOrigin = QPoint(0,0);
}
}
void KisCoordinatesConverter::getOpenGLCheckersInfo(const QRectF &viewportRect,
QTransform *textureTransform,
QTransform *modelTransform,
QRectF *textureRect,
QRectF *modelRect,
const bool scrollCheckers) const
{
if(scrollCheckers) {
*textureTransform = QTransform();
*textureRect = QRectF(0, 0, viewportRect.width(),viewportRect.height());
}
else {
*textureTransform = viewportToWidgetTransform();
*textureRect = viewportRect;
}
*modelTransform = viewportToWidgetTransform();
*modelRect = viewportRect;
}
QPointF KisCoordinatesConverter::imageCenterInWidgetPixel() const
{
if(!m_d->image)
return QPointF();
QPolygonF poly = imageToWidget(QPolygon(m_d->image->bounds()));
return (poly[0] + poly[1] + poly[2] + poly[3]) / 4.0;
}
// these functions return a bounding rect if the canvas is rotated
QRectF KisCoordinatesConverter::imageRectInWidgetPixels() const
{
if(!m_d->image) return QRectF();
return imageToWidget(m_d->image->bounds());
}
QRectF KisCoordinatesConverter::imageRectInViewportPixels() const
{
if(!m_d->image) return QRectF();
return imageToViewport(m_d->image->bounds());
}
QRect KisCoordinatesConverter::imageRectInImagePixels() const
{
if(!m_d->image) return QRect();
return m_d->image->bounds();
}
QRectF KisCoordinatesConverter::imageRectInDocumentPixels() const
{
if(!m_d->image) return QRectF();
return imageToDocument(m_d->image->bounds());
}
QSizeF KisCoordinatesConverter::imageSizeInFlakePixels() const
{
if(!m_d->image) return QSizeF();
qreal scaleX, scaleY;
imageScale(&scaleX, &scaleY);
QSize imageSize = m_d->image->size();
return QSizeF(imageSize.width() * scaleX, imageSize.height() * scaleY);
}
QRectF KisCoordinatesConverter::widgetRectInFlakePixels() const
{
return widgetToFlake(QRectF(QPoint(0,0), m_d->canvasWidgetSize));
}
QPointF KisCoordinatesConverter::flakeCenterPoint() const
{
QRectF widgetRect = widgetRectInFlakePixels();
return QPointF(widgetRect.left() + widgetRect.width() / 2,
widgetRect.top() + widgetRect.height() / 2);
}
QPointF KisCoordinatesConverter::widgetCenterPoint() const
{
return QPointF(m_d->canvasWidgetSize.width() / 2.0, m_d->canvasWidgetSize.height() / 2.0);
}
void KisCoordinatesConverter::imageScale(qreal *scaleX, qreal *scaleY) const
{
if(!m_d->image) {
*scaleX = 1.0;
*scaleY = 1.0;
return;
}
// get the x and y zoom level of the canvas
qreal zoomX, zoomY;
KoZoomHandler::zoom(&zoomX, &zoomY);
// Get the KisImage resolution
qreal resX = m_d->image->xRes();
qreal resY = m_d->image->yRes();
// Compute the scale factors
*scaleX = zoomX / resX;
*scaleY = zoomY / resY;
}
diff --git a/libs/ui/canvas/kis_coordinates_converter.h b/libs/ui/canvas/kis_coordinates_converter.h
index 803a5d94bd..e7ce76b2c9 100644
--- a/libs/ui/canvas/kis_coordinates_converter.h
+++ b/libs/ui/canvas/kis_coordinates_converter.h
@@ -1,162 +1,163 @@
/*
* Copyright (c) 2010 Dmitry Kazakov
* Copyright (c) 2011 Silvio Heinrich
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KIS_COORDINATES_CONVERTER_H
#define KIS_COORDINATES_CONVERTER_H
#include
#include
#include "kritaui_export.h"
#include "kis_types.h"
#define EPSILON 1e-6
#define SCALE_LESS_THAN(scX, scY, value) \
(scX < (value) - EPSILON && scY < (value) - EPSILON)
#define SCALE_MORE_OR_EQUAL_TO(scX, scY, value) \
(scX > (value) - EPSILON && scY > (value) - EPSILON)
namespace _Private
{
template struct Traits
{
typedef T Result;
static T map(const QTransform& transform, const T& obj) { return transform.map(obj); }
};
template<> struct Traits
{
typedef QRectF Result;
static QRectF map(const QTransform& transform, const QRectF& rc) { return transform.mapRect(rc); }
};
template<> struct Traits: public Traits { };
template<> struct Traits: public Traits { };
template<> struct Traits: public Traits { };
template<> struct Traits: public Traits { };
}
class KRITAUI_EXPORT KisCoordinatesConverter: public KoZoomHandler
{
public:
KisCoordinatesConverter();
~KisCoordinatesConverter() override;
void setCanvasWidgetSize(QSize size);
void setImage(KisImageWSP image);
void setDocumentOffset(const QPoint &offset);
QPoint documentOffset() const;
qreal rotationAngle() const;
QPoint rotate(QPointF center, qreal angle);
QPoint mirror(QPointF center, bool mirrorXAxis, bool mirrorYAxis);
bool xAxisMirrored() const;
bool yAxisMirrored() const;
QPoint resetRotation(QPointF center);
void setZoom(qreal zoom) override;
/**
* A composition of to scale methods: zoom level + image resolution
*/
qreal effectiveZoom() const;
template typename _Private::Traits::Result
imageToViewport(const T& obj) const { return _Private::Traits::map(imageToViewportTransform(), obj); }
template typename _Private::Traits::Result
viewportToImage(const T& obj) const { return _Private::Traits::map(imageToViewportTransform().inverted(), obj); }
template typename _Private::Traits::Result
flakeToWidget(const T& obj) const { return _Private::Traits::map(flakeToWidgetTransform(), obj); }
template typename _Private::Traits::Result
widgetToFlake(const T& obj) const { return _Private::Traits::map(flakeToWidgetTransform().inverted(), obj); }
template typename _Private::Traits::Result
widgetToViewport(const T& obj) const { return _Private::Traits::map(viewportToWidgetTransform().inverted(), obj); }
template typename _Private::Traits::Result
viewportToWidget(const T& obj) const { return _Private::Traits::map(viewportToWidgetTransform(), obj); }
template typename _Private::Traits::Result
documentToWidget(const T& obj) const { return _Private::Traits::map(documentToWidgetTransform(), obj); }
template typename _Private::Traits::Result
widgetToDocument(const T& obj) const { return _Private::Traits::map(documentToWidgetTransform().inverted(), obj); }
template typename _Private::Traits::Result
imageToDocument(const T& obj) const { return _Private::Traits::map(imageToDocumentTransform(), obj); }
template typename _Private::Traits::Result
documentToImage(const T& obj) const { return _Private::Traits::map(imageToDocumentTransform().inverted(), obj); }
template typename _Private::Traits::Result
documentToFlake(const T& obj) const { return _Private::Traits::map(documentToFlakeTransform(), obj); }
template typename _Private::Traits::Result
flakeToDocument(const T& obj) const { return _Private::Traits::map(documentToFlakeTransform().inverted(), obj); }
template typename _Private::Traits::Result
imageToWidget(const T& obj) const { return _Private::Traits::map(imageToWidgetTransform(), obj); }
template typename _Private::Traits::Result
widgetToImage(const T& obj) const { return _Private::Traits::map(imageToWidgetTransform().inverted(), obj); }
QTransform imageToWidgetTransform() const;
QTransform imageToDocumentTransform() const;
QTransform documentToFlakeTransform() const;
QTransform imageToViewportTransform() const;
QTransform viewportToWidgetTransform() const;
QTransform flakeToWidgetTransform() const;
QTransform documentToWidgetTransform() const;
void getQPainterCheckersInfo(QTransform *transform,
QPointF *brushOrigin,
- QPolygonF *poligon) const;
+ QPolygonF *poligon,
+ const bool scrollCheckers) const;
void getOpenGLCheckersInfo(const QRectF &viewportRect,
QTransform *textureTransform,
QTransform *modelTransform,
QRectF *textureRect,
QRectF *modelRect,
- bool scrollCheckers) const;
+ const bool scrollCheckers) const;
QPointF imageCenterInWidgetPixel() const;
QRectF imageRectInWidgetPixels() const;
QRectF imageRectInViewportPixels() const;
QSizeF imageSizeInFlakePixels() const;
QRectF widgetRectInFlakePixels() const;
QRect imageRectInImagePixels() const;
QRectF imageRectInDocumentPixels() const;
QPointF flakeCenterPoint() const;
QPointF widgetCenterPoint() const;
void imageScale(qreal *scaleX, qreal *scaleY) const;
private:
friend class KisZoomAndPanTest;
QPointF centeringCorrection() const;
void correctOffsetToTransformation();
void correctTransformationToOffset();
void recalculateTransformations();
private:
struct Private;
Private * const m_d;
};
#endif /* KIS_COORDINATES_CONVERTER_H */
diff --git a/libs/ui/canvas/kis_qpainter_canvas.cpp b/libs/ui/canvas/kis_qpainter_canvas.cpp
index 387aeb2c76..35ce05b83f 100644
--- a/libs/ui/canvas/kis_qpainter_canvas.cpp
+++ b/libs/ui/canvas/kis_qpainter_canvas.cpp
@@ -1,261 +1,266 @@
/*
* Copyright (C) Boudewijn Rempt , (C) 2006
* Copyright (C) Lukas Tvrdy , (C) 2009
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_qpainter_canvas.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include "kis_coordinates_converter.h"
#include
#include
#include
#include
#include
#include "KisViewManager.h"
#include "kis_canvas2.h"
#include "kis_prescaled_projection.h"
#include "kis_config.h"
#include "kis_canvas_resource_provider.h"
#include "KisDocument.h"
#include "kis_selection_manager.h"
#include "kis_selection.h"
#include "kis_canvas_updates_compressor.h"
#include "kis_config_notifier.h"
#include "kis_group_layer.h"
#include "canvas/kis_display_color_converter.h"
//#define DEBUG_REPAINT
#include
class KisQPainterCanvas::Private
{
public:
KisPrescaledProjectionSP prescaledProjection;
QBrush checkBrush;
QImage buffer;
+ bool scrollCheckers;
};
KisQPainterCanvas::KisQPainterCanvas(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent)
: QWidget(parent)
, KisCanvasWidgetBase(canvas, coordinatesConverter)
, m_d(new Private())
{
setAutoFillBackground(true);
setAcceptDrops(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_InputMethodEnabled, true);
setAttribute(Qt::WA_StaticContents);
setAttribute(Qt::WA_OpaquePaintEvent);
#ifdef Q_OS_OSX
setAttribute(Qt::WA_AcceptTouchEvents, false);
#else
setAttribute(Qt::WA_AcceptTouchEvents, true);
#endif
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
}
KisQPainterCanvas::~KisQPainterCanvas()
{
delete m_d;
}
void KisQPainterCanvas::setPrescaledProjection(KisPrescaledProjectionSP prescaledProjection)
{
m_d->prescaledProjection = prescaledProjection;
}
void KisQPainterCanvas::paintEvent(QPaintEvent * ev)
{
KisImageWSP image = canvas()->image();
if (image == 0) return;
setAutoFillBackground(false);
if (m_d->buffer.size() != size()) {
m_d->buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied);
}
QPainter gc(&m_d->buffer);
// we double buffer, so we paint on an image first, then from the image onto the canvas,
// so copy the clip region since otherwise we're filling the whole buffer every time with
// the background color _and_ the transparent squares.
gc.setClipRegion(ev->region());
KisCoordinatesConverter *converter = coordinatesConverter();
gc.save();
gc.setCompositionMode(QPainter::CompositionMode_Source);
gc.fillRect(QRect(QPoint(0, 0), size()), borderColor());
QTransform checkersTransform;
QPointF brushOrigin;
QPolygonF polygon;
- converter->getQPainterCheckersInfo(&checkersTransform, &brushOrigin, &polygon);
+ converter->getQPainterCheckersInfo(&checkersTransform, &brushOrigin, &polygon, m_d->scrollCheckers);
gc.setPen(Qt::NoPen);
gc.setBrush(m_d->checkBrush);
gc.setBrushOrigin(brushOrigin);
gc.setTransform(checkersTransform);
gc.drawPolygon(polygon);
drawImage(gc, ev->rect());
gc.restore();
#ifdef DEBUG_REPAINT
QColor color = QColor(random() % 255, random() % 255, random() % 255, 150);
gc.fillRect(ev->rect(), color);
#endif
drawDecorations(gc, ev->rect());
gc.end();
QPainter painter(this);
painter.drawImage(ev->rect(), m_d->buffer, ev->rect());
}
void KisQPainterCanvas::drawImage(QPainter & gc, const QRect &updateWidgetRect) const
{
KisCoordinatesConverter *converter = coordinatesConverter();
QTransform imageTransform = converter->viewportToWidgetTransform();
gc.setTransform(imageTransform);
gc.setRenderHint(QPainter::SmoothPixmapTransform, true);
QRectF viewportRect = converter->widgetToViewport(updateWidgetRect);
gc.setCompositionMode(QPainter::CompositionMode_SourceOver);
gc.drawImage(viewportRect, m_d->prescaledProjection->prescaledQImage(),
viewportRect);
}
QVariant KisQPainterCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
{
return processInputMethodQuery(query);
}
void KisQPainterCanvas::inputMethodEvent(QInputMethodEvent *event)
{
processInputMethodEvent(event);
}
void KisQPainterCanvas::channelSelectionChanged(const QBitArray &channelFlags)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setChannelFlags(channelFlags);
}
void KisQPainterCanvas::setDisplayProfile(KisDisplayColorConverter *colorConverter)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setMonitorProfile(colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
}
void KisQPainterCanvas::setDisplayFilter(QSharedPointer displayFilter)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setDisplayFilter(displayFilter);
canvas()->startUpdateInPatches(canvas()->image()->bounds());
}
void KisQPainterCanvas::setWrapAroundViewingMode(bool value)
{
Q_UNUSED(value);
dbgKrita << "Wrap around viewing mode not implemented in QPainter Canvas.";
return;
}
void KisQPainterCanvas::finishResizingImage(qint32 w, qint32 h)
{
m_d->prescaledProjection->slotImageSizeChanged(w, h);
}
KisUpdateInfoSP KisQPainterCanvas::startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags)
{
Q_UNUSED(channelFlags);
return m_d->prescaledProjection->updateCache(rc);
}
QRect KisQPainterCanvas::updateCanvasProjection(KisUpdateInfoSP info)
{
/**
* It might happen that the canvas type is switched while the
* update info is being stuck in the Qt's signals queue. Than a wrong
* type of the info may come. So just check it here.
*/
bool isPPUpdateInfo = dynamic_cast(info.data());
if (isPPUpdateInfo) {
m_d->prescaledProjection->recalculateCache(info);
return info->dirtyViewportRect();
} else {
return QRect();
}
}
void KisQPainterCanvas::resizeEvent(QResizeEvent *e)
{
QSize size(e->size());
if (size.width() <= 0) {
size.setWidth(1);
}
if (size.height() <= 0) {
size.setHeight(1);
}
coordinatesConverter()->setCanvasWidgetSize(size);
m_d->prescaledProjection->notifyCanvasSizeChanged(size);
}
void KisQPainterCanvas::slotConfigChanged()
{
+ KisConfig cfg;
+
m_d->checkBrush = QBrush(createCheckersImage());
+ m_d->scrollCheckers = cfg.scrollCheckers();
notifyConfigChanged();
}
bool KisQPainterCanvas::callFocusNextPrevChild(bool next)
{
return focusNextPrevChild(next);
}
diff --git a/libs/ui/forms/wdgnewimage.ui b/libs/ui/forms/wdgnewimage.ui
index 2fad6a6e25..63e3135f2c 100644
--- a/libs/ui/forms/wdgnewimage.ui
+++ b/libs/ui/forms/wdgnewimage.ui
@@ -1,683 +1,693 @@
WdgNewImage
0
0
600
- 422
+ 431
0
0
600
0
16777215
16777215
New Image
-
0
0
0
0
0
Dimensions
-
-
0
140
16777215
16777215
Image Size
Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
false
false
-
-
P&redefined:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
cmbPredefined
-
0
0
-
Save As:
-
0
0
-
Save the current dimensions
&Save
-
-
-
Landscape
...
true
true
true
-
2
1.000000000000000
100000000.000000000000000
-
&Height:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
doubleHeight
-
0
1.000000000000000
9999.000000000000000
-
Resolution:
-
1.000000000000000
100000000.000000000000000
-
-
-
pixels-per-inch
ppi
-
W&idth:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
doubleWidth
-
Portrait
...
true
true
true
true
-
-
Qt::Horizontal
QSizePolicy::Expanding
191
61
-
Clipboard
-
75
75
250
250
QFrame::StyledPanel
TextLabel
-
Qt::Vertical
20
40
-
-
&Name:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
txtName
-
untitled-1
-
0
0
Color
-
0
0
Content
-
Layers:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
Ima&ge Background Opacity:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
sliderOpacity
-
0
0
1
200
2
-
&Image Background Color:
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
cmbColor
-
Background:
Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing
-
&Description:
Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing
txtDescription
-
0
0
16777215
100
-
-
As fi&rst layer
-
As ca&nvas color
-
0
0
50
0
Reset the image background color in the Image Properties dialog
-
QFrame::NoFrame
QFrame::Plain
0
0
0
0
-
-
Qt::Vertical
20
40
label_4
lblBackgroundStyle
txtDescription
lblDescription
intNumLayers
opacityPanel
lblColor
cmbColor
lblOpacity
-
Qt::Vertical
QSizePolicy::Expanding
10
10
+ -
+
+
+ This document...
+
+
+ true
+
+
+
-
-
Qt::Horizontal
QSizePolicy::Expanding
480
10
-
&Create
true
true
KisIntParseSpinBox
QSpinBox
KisColorButton
QPushButton
KisDoubleSliderSpinBox
QWidget
1
KisColorSpaceSelector
QWidget
widgets/kis_color_space_selector.h
1
KisDoubleParseSpinBox
QDoubleSpinBox
kis_double_parse_spin_box.h
tabWidget
txtName
cmbPredefined
txtPredefinedName
bnSaveAsPredefined
doubleWidth
doubleHeight
doubleResolution
cmbWidthUnit
cmbHeightUnit
bnLandscape
bnPortrait
createButton
intNumLayers
cmbColor
radioBackgroundAsLayer
radioBackgroundAsProjection
txtDescription
diff --git a/libs/ui/kis_paintop_box.cc b/libs/ui/kis_paintop_box.cc
index 79a7c8a2be..059098a9a5 100644
--- a/libs/ui/kis_paintop_box.cc
+++ b/libs/ui/kis_paintop_box.cc
@@ -1,1276 +1,1291 @@
/*
* kis_paintop_box.cc - part of KImageShop/Krayon/Krita
*
* Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org)
* Copyright (c) 2009-2011 Sven Langkamp (sven.langkamp@gmail.com)
* Copyright (c) 2010 Lukáš Tvrdý
* Copyright (C) 2011 Silvio Heinrich
* Copyright (C) 2011 Srikanth Tiyyagura
* Copyright (c) 2014 Mohit Goyal
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_paintop_box.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_canvas2.h"
#include "kis_node_manager.h"
#include "KisViewManager.h"
#include "kis_canvas_resource_provider.h"
#include "kis_resource_server_provider.h"
#include "kis_favorite_resource_manager.h"
#include "kis_config.h"
#include "widgets/kis_popup_button.h"
#include "widgets/kis_tool_options_popup.h"
#include "widgets/kis_paintop_presets_popup.h"
#include "widgets/kis_tool_options_popup.h"
#include "widgets/kis_paintop_presets_chooser_popup.h"
#include "widgets/kis_workspace_chooser.h"
#include "widgets/kis_paintop_list_widget.h"
#include "widgets/kis_slider_spin_box.h"
#include "widgets/kis_cmb_composite.h"
#include "widgets/kis_widget_chooser.h"
#include "tool/kis_tool.h"
#include "kis_signals_blocker.h"
#include "kis_action_manager.h"
#include "kis_highlighted_button.h"
typedef KoResourceServerSimpleConstruction > KisPaintOpPresetResourceServer;
typedef KoResourceServerAdapter > KisPaintOpPresetResourceServerAdapter;
KisPaintopBox::KisPaintopBox(KisViewManager *view, QWidget *parent, const char *name)
: QWidget(parent)
, m_resourceProvider(view->resourceProvider())
, m_optionWidget(0)
, m_toolOptionsPopupButton(0)
, m_brushEditorPopupButton(0)
, m_presetSelectorPopupButton(0)
, m_toolOptionsPopup(0)
, m_viewManager(view)
, m_previousNode(0)
, m_currTabletToolID(KoInputDevice::invalid())
, m_presetsEnabled(true)
, m_blockUpdate(false)
, m_dirtyPresetsEnabled(false)
, m_eraserBrushSizeEnabled(false)
, m_eraserBrushOpacityEnabled(false)
{
Q_ASSERT(view != 0);
setObjectName(name);
KisConfig cfg;
m_dirtyPresetsEnabled = cfg.useDirtyPresets();
m_eraserBrushSizeEnabled = cfg.useEraserBrushSize();
m_eraserBrushOpacityEnabled = cfg.useEraserBrushOpacity();
KAcceleratorManager::setNoAccel(this);
setWindowTitle(i18n("Painter's Toolchest"));
m_favoriteResourceManager = new KisFavoriteResourceManager(this);
KConfigGroup grp = KSharedConfig::openConfig()->group("krita").group("Toolbar BrushesAndStuff");
int iconsize = grp.readEntry("IconSize", 32);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopupButton = new KisPopupButton(this);
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
m_toolOptionsPopupButton->setToolTip(i18n("Tool Settings"));
m_toolOptionsPopupButton->setFixedSize(iconsize, iconsize);
}
m_brushEditorPopupButton = new KisPopupButton(this);
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_brushEditorPopupButton->setToolTip(i18n("Edit brush settings"));
m_brushEditorPopupButton->setFixedSize(iconsize, iconsize);
m_presetSelectorPopupButton = new KisPopupButton(this);
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_presetSelectorPopupButton->setToolTip(i18n("Choose brush preset"));
m_presetSelectorPopupButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton = new KisHighlightedToolButton(this);
m_eraseModeButton->setFixedSize(iconsize, iconsize);
m_eraseModeButton->setCheckable(true);
m_eraseAction = m_viewManager->actionManager()->createAction("erase_action");
m_eraseModeButton->setDefaultAction(m_eraseAction);
m_reloadButton = new QToolButton(this);
m_reloadButton->setFixedSize(iconsize, iconsize);
m_reloadAction = m_viewManager->actionManager()->createAction("reload_preset_action");
m_reloadButton->setDefaultAction(m_reloadAction);
m_alphaLockButton = new KisHighlightedToolButton(this);
m_alphaLockButton->setFixedSize(iconsize, iconsize);
m_alphaLockButton->setCheckable(true);
KisAction* alphaLockAction = m_viewManager->actionManager()->createAction("preserve_alpha");
m_alphaLockButton->setDefaultAction(alphaLockAction);
// pen pressure
m_disablePressureButton = new KisHighlightedToolButton(this);
m_disablePressureButton->setFixedSize(iconsize, iconsize);
m_disablePressureButton->setCheckable(true);
m_disablePressureAction = m_viewManager->actionManager()->createAction("disable_pressure");
m_disablePressureButton->setDefaultAction(m_disablePressureAction);
// horizontal and vertical mirror toolbar buttons
// mirror tool options for the X Mirror
QMenu *toolbarMenuXMirror = new QMenu();
hideCanvasDecorationsX = m_viewManager->actionManager()->createAction("mirrorX-hideDecorations");
toolbarMenuXMirror->addAction(hideCanvasDecorationsX);
lockActionX = m_viewManager->actionManager()->createAction("mirrorX-lock");
toolbarMenuXMirror->addAction(lockActionX);
moveToCenterActionX = m_viewManager->actionManager()->createAction("mirrorX-moveToCenter");
toolbarMenuXMirror->addAction(moveToCenterActionX);
// mirror tool options for the Y Mirror
QMenu *toolbarMenuYMirror = new QMenu();
hideCanvasDecorationsY = m_viewManager->actionManager()->createAction("mirrorY-hideDecorations");
toolbarMenuYMirror->addAction(hideCanvasDecorationsY);
lockActionY = m_viewManager->actionManager()->createAction("mirrorY-lock");
toolbarMenuYMirror->addAction(lockActionY);
moveToCenterActionY = m_viewManager->actionManager()->createAction("mirrorY-moveToCenter");
toolbarMenuYMirror->addAction(moveToCenterActionY);
// create horizontal and vertical mirror buttons
m_hMirrorButton = new KisHighlightedToolButton(this);
int menuPadding = 10;
m_hMirrorButton->setFixedSize(iconsize + menuPadding, iconsize);
m_hMirrorButton->setCheckable(true);
m_hMirrorAction = m_viewManager->actionManager()->createAction("hmirror_action");
m_hMirrorButton->setDefaultAction(m_hMirrorAction);
m_hMirrorButton->setMenu(toolbarMenuXMirror);
m_hMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
m_vMirrorButton = new KisHighlightedToolButton(this);
m_vMirrorButton->setFixedSize(iconsize + menuPadding, iconsize);
m_vMirrorButton->setCheckable(true);
m_vMirrorAction = m_viewManager->actionManager()->createAction("vmirror_action");
m_vMirrorButton->setDefaultAction(m_vMirrorAction);
m_vMirrorButton->setMenu(toolbarMenuYMirror);
m_vMirrorButton->setPopupMode(QToolButton::MenuButtonPopup);
// add connections for horizontal and mirrror buttons
connect(lockActionX, SIGNAL(toggled(bool)), this, SLOT(slotLockXMirrorToggle(bool)));
connect(lockActionY, SIGNAL(toggled(bool)), this, SLOT(slotLockYMirrorToggle(bool)));
connect(moveToCenterActionX, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorX()));
connect(moveToCenterActionY, SIGNAL(triggered(bool)), this, SLOT(slotMoveToCenterMirrorY()));
connect(hideCanvasDecorationsX, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorX(bool)));
connect(hideCanvasDecorationsY, SIGNAL(toggled(bool)), this, SLOT(slotHideDecorationMirrorY(bool)));
const bool sliderLabels = cfg.sliderLabels();
int sliderWidth;
if (sliderLabels) {
sliderWidth = 150 * logicalDpiX() / 96;
}
else {
sliderWidth = 120 * logicalDpiX() / 96;
}
for (int i = 0; i < 3; ++i) {
m_sliderChooser[i] = new KisWidgetChooser(i + 1);
KisDoubleSliderSpinBox* slOpacity;
KisDoubleSliderSpinBox* slFlow;
KisDoubleSliderSpinBox* slSize;
if (sliderLabels) {
slOpacity = m_sliderChooser[i]->addWidget("opacity");
slFlow = m_sliderChooser[i]->addWidget("flow");
slSize = m_sliderChooser[i]->addWidget("size");
slOpacity->setPrefix(QString("%1 ").arg(i18n("Opacity:")));
slFlow->setPrefix(QString("%1 ").arg(i18n("Flow:")));
slSize->setPrefix(QString("%1 ").arg(i18n("Size:")));
}
else {
slOpacity = m_sliderChooser[i]->addWidget("opacity", i18n("Opacity:"));
slFlow = m_sliderChooser[i]->addWidget("flow", i18n("Flow:"));
slSize = m_sliderChooser[i]->addWidget("size", i18n("Size:"));
}
slOpacity->setRange(0.0, 1.0, 2);
slOpacity->setValue(1.0);
slOpacity->setSingleStep(0.05);
slOpacity->setMinimumWidth(qMax(sliderWidth, slOpacity->sizeHint().width()));
slOpacity->setFixedHeight(iconsize);
slOpacity->setBlockUpdateSignalOnDrag(true);
slFlow->setRange(0.0, 1.0, 2);
slFlow->setValue(1.0);
slFlow->setSingleStep(0.05);
slFlow->setMinimumWidth(qMax(sliderWidth, slFlow->sizeHint().width()));
slFlow->setFixedHeight(iconsize);
slFlow->setBlockUpdateSignalOnDrag(true);
slSize->setRange(0, cfg.readEntry("maximumBrushSize", 1000), 2);
slSize->setValue(100);
slSize->setSingleStep(1);
slSize->setExponentRatio(3.0);
slSize->setSuffix(i18n(" px"));
slSize->setMinimumWidth(qMax(sliderWidth, slSize->sizeHint().width()));
slSize->setFixedHeight(iconsize);
slSize->setBlockUpdateSignalOnDrag(true);
m_sliderChooser[i]->chooseWidget(cfg.toolbarSlider(i + 1));
}
m_cmbCompositeOp = new KisCompositeOpComboBox();
m_cmbCompositeOp->setFixedHeight(iconsize);
Q_FOREACH (KisAction * a, m_cmbCompositeOp->blendmodeActions()) {
m_viewManager->actionManager()->addAction(a->text(), a);
}
m_workspaceWidget = new KisPopupButton(this);
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
m_workspaceWidget->setToolTip(i18n("Choose workspace"));
m_workspaceWidget->setFixedSize(iconsize, iconsize);
m_workspaceWidget->setPopupWidget(new KisWorkspaceChooser(view));
QHBoxLayout* baseLayout = new QHBoxLayout(this);
m_paintopWidget = new QWidget(this);
baseLayout->addWidget(m_paintopWidget);
baseLayout->setSpacing(4);
baseLayout->setContentsMargins(0, 0, 0, 0);
m_layout = new QHBoxLayout(m_paintopWidget);
if (!cfg.toolOptionsInDocker()) {
m_layout->addWidget(m_toolOptionsPopupButton);
}
m_layout->addWidget(m_brushEditorPopupButton);
m_layout->addWidget(m_presetSelectorPopupButton);
m_layout->setSpacing(4);
m_layout->setContentsMargins(0, 0, 0, 0);
QWidget* compositeActions = new QWidget(this);
QHBoxLayout* compositeLayout = new QHBoxLayout(compositeActions);
compositeLayout->addWidget(m_cmbCompositeOp);
compositeLayout->addWidget(m_eraseModeButton);
compositeLayout->addWidget(m_alphaLockButton);
compositeLayout->setSpacing(4);
compositeLayout->setContentsMargins(0, 0, 0, 0);
compositeLayout->addWidget(m_reloadButton);
QWidgetAction * action;
action = new QWidgetAction(this);
view->actionCollection()->addAction("composite_actions", action);
action->setText(i18n("Brush composite"));
action->setDefaultWidget(compositeActions);
QWidget* compositePressure = new QWidget(this);
QHBoxLayout* pressureLayout = new QHBoxLayout(compositePressure);
pressureLayout->addWidget(m_disablePressureButton);
pressureLayout->setSpacing(4);
pressureLayout->setContentsMargins(0, 0, 0, 0);
action = new QWidgetAction(this);
view->actionCollection()->addAction("pressure_action", action);
action->setText(i18n("Pressure usage (small button)"));
action->setDefaultWidget(compositePressure);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider1", action);
view->actionCollection()->addAction("brushslider1", action);
action->setDefaultWidget(m_sliderChooser[0]);
connect(action, SIGNAL(triggered()), m_sliderChooser[0], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[0], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider2", action);
view->actionCollection()->addAction("brushslider2", action);
action->setDefaultWidget(m_sliderChooser[1]);
connect(action, SIGNAL(triggered()), m_sliderChooser[1], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[1], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("brushslider3", action);
view->actionCollection()->addAction("brushslider3", action);
action->setDefaultWidget(m_sliderChooser[2]);
connect(action, SIGNAL(triggered()), m_sliderChooser[2], SLOT(showPopupWidget()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_sliderChooser[2], SLOT(updateThemedIcons()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("next_favorite_preset", action);
view->actionCollection()->addAction("next_favorite_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotNextFavoritePreset()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("previous_favorite_preset", action);
view->actionCollection()->addAction("previous_favorite_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotPreviousFavoritePreset()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("previous_preset", action);
view->actionCollection()->addAction("previous_preset", action);
connect(action, SIGNAL(triggered()), this, SLOT(slotSwitchToPreviousPreset()));
if (!cfg.toolOptionsInDocker()) {
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_tool_options", action);
view->actionCollection()->addAction("show_tool_options", action);
connect(action, SIGNAL(triggered()), m_toolOptionsPopupButton, SLOT(showPopupWidget()));
}
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_brush_editor", action);
view->actionCollection()->addAction("show_brush_editor", action);
connect(action, SIGNAL(triggered()), m_brushEditorPopupButton, SLOT(showPopupWidget()));
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("show_brush_presets", action);
view->actionCollection()->addAction("show_brush_presets", action);
connect(action, SIGNAL(triggered()), m_presetSelectorPopupButton, SLOT(showPopupWidget()));
QWidget* mirrorActions = new QWidget(this);
QHBoxLayout* mirrorLayout = new QHBoxLayout(mirrorActions);
mirrorLayout->addWidget(m_hMirrorButton);
mirrorLayout->addWidget(m_vMirrorButton);
mirrorLayout->setSpacing(4);
mirrorLayout->setContentsMargins(0, 0, 0, 0);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("mirror_actions", action);
action->setDefaultWidget(mirrorActions);
view->actionCollection()->addAction("mirror_actions", action);
action = new QWidgetAction(this);
KisActionRegistry::instance()->propertizeAction("workspaces", action);
view->actionCollection()->addAction("workspaces", action);
action->setDefaultWidget(m_workspaceWidget);
if (!cfg.toolOptionsInDocker()) {
m_toolOptionsPopup = new KisToolOptionsPopup();
m_toolOptionsPopupButton->setPopupWidget(m_toolOptionsPopup);
m_toolOptionsPopup->switchDetached(false);
}
m_savePresetWidget = new KisPresetSaveWidget(this);
m_presetsPopup = new KisPaintOpPresetsPopup(m_resourceProvider, m_favoriteResourceManager, m_savePresetWidget);
m_brushEditorPopupButton->setPopupWidget(m_presetsPopup);
m_presetsPopup->parentWidget()->setWindowTitle(i18n("Brush Editor"));
connect(m_presetsPopup, SIGNAL(brushEditorShown()), SLOT(slotUpdateOptionsWidgetPopup()));
connect(m_viewManager->mainWindow(), SIGNAL(themeChanged()), m_presetsPopup, SLOT(updateThemedIcons()));
m_presetsChooserPopup = new KisPaintOpPresetsChooserPopup();
m_presetSelectorPopupButton->setPopupWidget(m_presetsChooserPopup);
m_currCompositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
slotNodeChanged(view->activeNode());
// Get all the paintops
QList keys = KisPaintOpRegistry::instance()->keys();
QList factoryList;
Q_FOREACH (const QString & paintopId, keys) {
factoryList.append(KisPaintOpRegistry::instance()->get(paintopId));
}
m_presetsPopup->setPaintOpList(factoryList);
connect(m_presetsPopup , SIGNAL(paintopActivated(QString)) , SLOT(slotSetPaintop(QString)));
connect(m_presetsPopup , SIGNAL(defaultPresetClicked()) , SLOT(slotSetupDefaultPreset()));
connect(m_presetsPopup , SIGNAL(signalResourceSelected(KoResource*)), SLOT(resourceSelected(KoResource*)));
connect(m_presetsPopup , SIGNAL(reloadPresetClicked()) , SLOT(slotReloadPreset()));
connect(m_presetsPopup , SIGNAL(dirtyPresetToggled(bool)) , SLOT(slotDirtyPresetToggled(bool)));
connect(m_presetsPopup , SIGNAL(eraserBrushSizeToggled(bool)) , SLOT(slotEraserBrushSizeToggled(bool)));
connect(m_presetsPopup , SIGNAL(eraserBrushOpacityToggled(bool)) , SLOT(slotEraserBrushOpacityToggled(bool)));
connect(m_presetsChooserPopup, SIGNAL(resourceSelected(KoResource*)) , SLOT(resourceSelected(KoResource*)));
connect(m_presetsChooserPopup, SIGNAL(resourceClicked(KoResource*)) , SLOT(resourceSelected(KoResource*)));
connect(m_resourceProvider , SIGNAL(sigNodeChanged(const KisNodeSP)) , SLOT(slotNodeChanged(const KisNodeSP)));
connect(m_cmbCompositeOp , SIGNAL(currentIndexChanged(int)) , SLOT(slotSetCompositeMode(int)));
connect(m_eraseAction , SIGNAL(toggled(bool)) , SLOT(slotToggleEraseMode(bool)));
connect(alphaLockAction , SIGNAL(toggled(bool)) , SLOT(slotToggleAlphaLockMode(bool)));
connect(m_disablePressureAction , SIGNAL(toggled(bool)) , SLOT(slotDisablePressureMode(bool)));
m_disablePressureAction->setChecked(true);
connect(m_hMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotHorizontalMirrorChanged(bool)));
connect(m_vMirrorAction , SIGNAL(toggled(bool)) , SLOT(slotVerticalMirrorChanged(bool)));
connect(m_reloadAction , SIGNAL(triggered()) , SLOT(slotReloadPreset()));
connect(m_sliderChooser[0]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[0]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider1Changed()));
connect(m_sliderChooser[1]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[1]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider2Changed()));
connect(m_sliderChooser[2]->getWidget("opacity"), SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget("flow") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
connect(m_sliderChooser[2]->getWidget("size") , SIGNAL(valueChanged(qreal)), SLOT(slotSlider3Changed()));
//Needed to connect canvas to favorite resource manager
connect(m_viewManager->resourceProvider(), SIGNAL(sigFGColorChanged(KoColor)), SLOT(slotUnsetEraseMode()));
connect(m_resourceProvider, SIGNAL(sigFGColorUsed(KoColor)), m_favoriteResourceManager, SLOT(slotAddRecentColor(KoColor)));
connect(m_resourceProvider, SIGNAL(sigFGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotChangeFGColorSelector(KoColor)));
connect(m_resourceProvider, SIGNAL(sigBGColorChanged(KoColor)), m_favoriteResourceManager, SLOT(slotSetBGColor(KoColor)));
// cold initialization
m_favoriteResourceManager->slotChangeFGColorSelector(m_resourceProvider->fgColor());
m_favoriteResourceManager->slotSetBGColor(m_resourceProvider->bgColor());
connect(m_favoriteResourceManager, SIGNAL(sigSetFGColor(KoColor)), m_resourceProvider, SLOT(slotSetFGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigSetBGColor(KoColor)), m_resourceProvider, SLOT(slotSetBGColor(KoColor)));
connect(m_favoriteResourceManager, SIGNAL(sigEnableChangeColor(bool)), m_resourceProvider, SLOT(slotResetEnableFGChange(bool)));
connect(view->mainWindow(), SIGNAL(themeChanged()), this, SLOT(slotUpdateSelectionIcon()));
slotInputDeviceChanged(KoToolManager::instance()->currentInputDevice());
}
KisPaintopBox::~KisPaintopBox()
{
KisConfig cfg;
QMapIterator iter(m_tabletToolMap);
while (iter.hasNext()) {
iter.next();
//qDebug() << "Writing last used preset for" << iter.key().pointer << iter.key().uniqueID << iter.value().preset->name();
if ((iter.key().pointer) == QTabletEvent::Eraser) {
cfg.writeEntry(QString("LastEraser_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
else {
cfg.writeEntry(QString("LastPreset_%1").arg(iter.key().uniqueID) , iter.value().preset->name());
}
}
// Do not delete the widget, since it it is global to the application, not owned by the view
m_presetsPopup->setPaintOpSettingsWidget(0);
qDeleteAll(m_paintopOptionWidgets);
delete m_favoriteResourceManager;
for (int i = 0; i < 3; ++i) {
delete m_sliderChooser[i];
}
}
void KisPaintopBox::restoreResource(KoResource* resource)
{
KisPaintOpPreset* preset = dynamic_cast(resource);
//qDebug() << "restoreResource" << resource << preset;
if (preset) {
setCurrentPaintop(preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
void KisPaintopBox::newOptionWidgets(const QList > &optionWidgetList)
{
if (m_toolOptionsPopup) {
m_toolOptionsPopup->newOptionWidgets(optionWidgetList);
}
}
void KisPaintopBox::resourceSelected(KoResource* resource)
{
KisPaintOpPreset* preset = dynamic_cast(resource);
if (preset && preset != m_resourceProvider->currentPreset()) {
if (!preset->settings()->isLoadable())
return;
if (!m_dirtyPresetsEnabled) {
KisSignalsBlocker blocker(m_optionWidget);
if (!preset->load()) {
warnKrita << "failed to load the preset.";
}
}
//qDebug() << "resourceSelected" << resource->name();
setCurrentPaintop(preset);
m_presetsPopup->setPresetImage(preset->image());
m_presetsPopup->resourceSelected(resource);
}
}
void KisPaintopBox::setCurrentPaintop(const KoID& paintop)
{
KisPaintOpPresetSP preset = activePreset(paintop);
Q_ASSERT(preset && preset->settings());
//qDebug() << "setCurrentPaintop();" << paintop << preset;
setCurrentPaintop(preset);
}
void KisPaintopBox::setCurrentPaintop(KisPaintOpPresetSP preset)
{
//qDebug() << "setCurrentPaintop(); " << preset->name();
if (preset == m_resourceProvider->currentPreset()) {
if (preset == m_tabletToolMap[m_currTabletToolID].preset) {
return;
}
}
Q_ASSERT(preset);
const KoID& paintop = preset->paintOp();
m_presetConnections.clear();
if (m_resourceProvider->currentPreset()) {
m_resourceProvider->setPreviousPaintOpPreset(m_resourceProvider->currentPreset());
if (m_optionWidget) {
m_optionWidget->hide();
}
}
if (!m_paintopOptionWidgets.contains(paintop))
m_paintopOptionWidgets[paintop] = KisPaintOpRegistry::instance()->get(paintop.id())->createConfigWidget(this);
m_optionWidget = m_paintopOptionWidgets[paintop];
KisSignalsBlocker b(m_optionWidget);
preset->setOptionsWidget(m_optionWidget);
m_optionWidget->setImage(m_viewManager->image());
m_optionWidget->setNode(m_viewManager->activeNode());
m_presetsPopup->setPaintOpSettingsWidget(m_optionWidget);
m_resourceProvider->setPaintOpPreset(preset);
Q_ASSERT(m_optionWidget && m_presetSelectorPopupButton);
m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotGuiChangedCurrentPreset()));
m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigSaveLockedConfig(KisPropertiesConfigurationSP)), this, SLOT(slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP)));
m_presetConnections.addConnection(m_optionWidget, SIGNAL(sigDropLockedConfig(KisPropertiesConfigurationSP)), this, SLOT(slotDropLockedOption(KisPropertiesConfigurationSP)));
// load the current brush engine icon for the brush editor toolbar button
KisPaintOpFactory* paintOp = KisPaintOpRegistry::instance()->get(paintop.id());
QString pixFilename = KoResourcePaths::findResource("kis_images", paintOp->pixmap());
m_brushEditorPopupButton->setIcon(QIcon(pixFilename));
m_presetsPopup->setCurrentPaintOpId(paintop.id());
////qDebug() << "\tsetting the new preset for" << m_currTabletToolID.uniqueID << "to" << preset->name();
m_paintOpPresetMap[m_resourceProvider->currentPreset()->paintOp()] = preset;
m_tabletToolMap[m_currTabletToolID].preset = preset;
m_tabletToolMap[m_currTabletToolID].paintOpID = preset->paintOp();
if (m_presetsPopup->currentPaintOpId() != paintop.id()) {
// Must change the paintop as the current one is not supported
// by the new colorspace.
dbgKrita << "current paintop " << paintop.name() << " was not set, not supported by colorspace";
}
}
void KisPaintopBox::slotUpdateOptionsWidgetPopup()
{
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
KIS_SAFE_ASSERT_RECOVER_RETURN(preset);
KIS_SAFE_ASSERT_RECOVER_RETURN(m_optionWidget);
m_optionWidget->setConfigurationSafe(preset->settings());
m_presetsPopup->resourceSelected(preset.data());
m_presetsPopup->updateViewSettings();
// the m_viewManager->image() is set earlier, but the reference will be missing when the stamp button is pressed
// need to later do some research on how and when we should be using weak shared pointers (WSP) that creates this situation
m_optionWidget->setImage(m_viewManager->image());
}
KisPaintOpPresetSP KisPaintopBox::defaultPreset(const KoID& paintOp)
{
QString defaultName = paintOp.id() + ".kpp";
QString path = KoResourcePaths::findResource("kis_defaultpresets", defaultName);
KisPaintOpPresetSP preset = new KisPaintOpPreset(path);
if (!preset->load()) {
preset = KisPaintOpRegistry::instance()->defaultPreset(paintOp);
}
Q_ASSERT(preset);
Q_ASSERT(preset->valid());
return preset;
}
KisPaintOpPresetSP KisPaintopBox::activePreset(const KoID& paintOp)
{
if (m_paintOpPresetMap[paintOp] == 0) {
m_paintOpPresetMap[paintOp] = defaultPreset(paintOp);
}
return m_paintOpPresetMap[paintOp];
}
void KisPaintopBox::updateCompositeOp(QString compositeOpID)
{
if (!m_optionWidget) return;
KisSignalsBlocker blocker(m_optionWidget);
KisNodeSP node = m_resourceProvider->currentNode();
if (node && node->paintDevice()) {
if (!node->paintDevice()->colorSpace()->hasCompositeOp(compositeOpID))
compositeOpID = KoCompositeOpRegistry::instance().getDefaultCompositeOp().id();
{
KisSignalsBlocker b1(m_cmbCompositeOp);
m_cmbCompositeOp->selectCompositeOp(KoID(compositeOpID));
}
if (compositeOpID != m_currCompositeOpID) {
m_currCompositeOpID = compositeOpID;
}
if (compositeOpID == COMPOSITE_ERASE) {
m_eraseModeButton->setChecked(true);
}
else {
m_eraseModeButton->setChecked(false);
}
}
}
void KisPaintopBox::setWidgetState(int flags)
{
if (flags & (ENABLE_COMPOSITEOP | DISABLE_COMPOSITEOP)) {
m_cmbCompositeOp->setEnabled(flags & ENABLE_COMPOSITEOP);
m_eraseModeButton->setEnabled(flags & ENABLE_COMPOSITEOP);
}
if (flags & (ENABLE_PRESETS | DISABLE_PRESETS)) {
m_presetSelectorPopupButton->setEnabled(flags & ENABLE_PRESETS);
m_brushEditorPopupButton->setEnabled(flags & ENABLE_PRESETS);
}
for (int i = 0; i < 3; ++i) {
if (flags & (ENABLE_OPACITY | DISABLE_OPACITY))
m_sliderChooser[i]->getWidget("opacity")->setEnabled(flags & ENABLE_OPACITY);
if (flags & (ENABLE_FLOW | DISABLE_FLOW))
m_sliderChooser[i]->getWidget("flow")->setEnabled(flags & ENABLE_FLOW);
if (flags & (ENABLE_SIZE | DISABLE_SIZE))
m_sliderChooser[i]->getWidget("size")->setEnabled(flags & ENABLE_SIZE);
}
}
void KisPaintopBox::setSliderValue(const QString& sliderID, qreal value)
{
for (int i = 0; i < 3; ++i) {
KisDoubleSliderSpinBox* slider = m_sliderChooser[i]->getWidget(sliderID);
KisSignalsBlocker b(slider);
slider->setValue(value);
}
}
void KisPaintopBox::slotSetPaintop(const QString& paintOpId)
{
if (KisPaintOpRegistry::instance()->get(paintOpId) != 0) {
KoID id(paintOpId, KisPaintOpRegistry::instance()->get(paintOpId)->name());
//qDebug() << "slotsetpaintop" << id;
setCurrentPaintop(id);
}
}
void KisPaintopBox::slotInputDeviceChanged(const KoInputDevice& inputDevice)
{
TabletToolMap::iterator toolData = m_tabletToolMap.find(inputDevice);
//qDebug() << "slotInputDeviceChanged()" << inputDevice.device() << inputDevice.uniqueTabletId();
m_currTabletToolID = TabletToolID(inputDevice);
if (toolData == m_tabletToolMap.end()) {
KisConfig cfg;
KisPaintOpPresetResourceServer *rserver = KisResourceServerProvider::instance()->paintOpPresetServer(false);
KisPaintOpPresetSP preset;
if (inputDevice.pointer() == QTabletEvent::Eraser) {
preset = rserver->resourceByName(cfg.readEntry(QString("LastEraser_%1").arg(inputDevice.uniqueTabletId()), "Eraser_circle"));
}
else {
preset = rserver->resourceByName(cfg.readEntry(QString("LastPreset_%1").arg(inputDevice.uniqueTabletId()), "Basic_tip_default"));
//if (preset)
//qDebug() << "found stored preset " << preset->name() << "for" << inputDevice.uniqueTabletId();
//else
//qDebug() << "no preset fcound for" << inputDevice.uniqueTabletId();
}
if (!preset) {
preset = rserver->resourceByName("Basic_tip_default");
}
if (preset) {
//qDebug() << "inputdevicechanged 1" << preset;
setCurrentPaintop(preset);
}
}
else {
if (toolData->preset) {
//qDebug() << "inputdevicechanged 2" << toolData->preset;
setCurrentPaintop(toolData->preset);
}
else {
//qDebug() << "inputdevicechanged 3" << toolData->paintOpID;
setCurrentPaintop(toolData->paintOpID);
}
}
}
void KisPaintopBox::slotCanvasResourceChanged(int key, const QVariant &value)
{
if (m_viewManager) {
sender()->blockSignals(true);
KisPaintOpPresetSP preset = m_viewManager->resourceProvider()->resourceManager()->resource(KisCanvasResourceProvider::CurrentPaintOpPreset).value();
if (preset && m_resourceProvider->currentPreset()->name() != preset->name()) {
QString compositeOp = preset->settings()->getString("CompositeOp");
updateCompositeOp(compositeOp);
resourceSelected(preset.data());
}
/**
* Update currently selected preset in both the popup widgets
*/
m_presetsChooserPopup->canvasResourceChanged(preset);
m_presetsPopup->currentPresetChanged(preset);
if (key == KisCanvasResourceProvider::CurrentCompositeOp) {
if (m_resourceProvider->currentCompositeOp() != m_currCompositeOpID) {
updateCompositeOp(m_resourceProvider->currentCompositeOp());
}
}
if (key == KisCanvasResourceProvider::Size) {
setSliderValue("size", m_resourceProvider->size());
}
if (key == KisCanvasResourceProvider::Opacity) {
setSliderValue("opacity", m_resourceProvider->opacity());
}
if (key == KisCanvasResourceProvider::Flow) {
setSliderValue("flow", m_resourceProvider->flow());
}
if (key == KisCanvasResourceProvider::EraserMode) {
m_eraseAction->setChecked(value.toBool());
}
if (key == KisCanvasResourceProvider::DisablePressure) {
m_disablePressureAction->setChecked(value.toBool());
}
sender()->blockSignals(false);
}
}
void KisPaintopBox::slotUpdatePreset()
{
if (!m_resourceProvider->currentPreset()) return;
// block updates of avoid some over updating of the option widget
m_blockUpdate = true;
setSliderValue("size", m_resourceProvider->size());
{
qreal opacity = m_resourceProvider->currentPreset()->settings()->paintOpOpacity();
m_resourceProvider->setOpacity(opacity);
setSliderValue("opacity", opacity);
setWidgetState(ENABLE_OPACITY);
}
{
setSliderValue("flow", m_resourceProvider->currentPreset()->settings()->paintOpFlow());
setWidgetState(ENABLE_FLOW);
}
{
updateCompositeOp(m_resourceProvider->currentPreset()->settings()->paintOpCompositeOp());
setWidgetState(ENABLE_COMPOSITEOP);
}
m_blockUpdate = false;
}
void KisPaintopBox::slotSetupDefaultPreset()
{
KisPaintOpPresetSP preset = defaultPreset(m_resourceProvider->currentPreset()->paintOp());
preset->setOptionsWidget(m_optionWidget);
m_resourceProvider->setPaintOpPreset(preset);
}
void KisPaintopBox::slotNodeChanged(const KisNodeSP node)
{
if (m_previousNode.isValid() && m_previousNode->paintDevice())
disconnect(m_previousNode->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
// Reconnect colorspace change of node
if (node && node->paintDevice()) {
connect(node->paintDevice().data(), SIGNAL(colorSpaceChanged(const KoColorSpace*)), this, SLOT(slotColorSpaceChanged(const KoColorSpace*)));
m_resourceProvider->setCurrentCompositeOp(m_currCompositeOpID);
m_previousNode = node;
slotColorSpaceChanged(node->colorSpace());
}
if (m_optionWidget) {
m_optionWidget->setNode(node);
}
}
void KisPaintopBox::slotColorSpaceChanged(const KoColorSpace* colorSpace)
{
m_cmbCompositeOp->validate(colorSpace);
}
void KisPaintopBox::slotToggleEraseMode(bool checked)
{
const bool oldEraserMode = m_resourceProvider->eraserMode();
m_resourceProvider->setEraserMode(checked);
if (oldEraserMode != checked && m_eraserBrushSizeEnabled) {
const qreal currentSize = m_resourceProvider->size();
KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
// remember brush size. set the eraser size to the normal brush size if not set
if (checked) {
settings->setSavedBrushSize(currentSize);
if (qFuzzyIsNull(settings->savedEraserSize())) {
settings->setSavedEraserSize(currentSize);
}
} else {
settings->setSavedEraserSize(currentSize);
if (qFuzzyIsNull(settings->savedBrushSize())) {
settings->setSavedBrushSize(currentSize);
}
}
//update value in UI (this is the main place the value is 'stored' in memory)
qreal newSize = checked ? settings->savedEraserSize() : settings->savedBrushSize();
m_resourceProvider->setSize(newSize);
}
if (oldEraserMode != checked && m_eraserBrushOpacityEnabled) {
const qreal currentOpacity = m_resourceProvider->opacity();
KisPaintOpSettingsSP settings = m_resourceProvider->currentPreset()->settings();
// remember brush opacity. set the eraser opacity to the normal brush opacity if not set
if (checked) {
settings->setSavedBrushOpacity(currentOpacity);
if (qFuzzyIsNull(settings->savedEraserOpacity())) {
settings->setSavedEraserOpacity(currentOpacity);
}
} else {
settings->setSavedEraserOpacity(currentOpacity);
if (qFuzzyIsNull(settings->savedBrushOpacity())) {
settings->setSavedBrushOpacity(currentOpacity);
}
}
//update value in UI (this is the main place the value is 'stored' in memory)
qreal newOpacity = checked ? settings->savedEraserOpacity() : settings->savedBrushOpacity();
m_resourceProvider->setOpacity(newOpacity);
}
}
void KisPaintopBox::slotSetCompositeMode(int index)
{
Q_UNUSED(index);
QString compositeOp = m_cmbCompositeOp->selectedCompositeOp().id();
m_resourceProvider->setCurrentCompositeOp(compositeOp);
}
void KisPaintopBox::slotHorizontalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorHorizontal(value);
}
void KisPaintopBox::slotVerticalMirrorChanged(bool value)
{
m_resourceProvider->setMirrorVertical(value);
}
void KisPaintopBox::sliderChanged(int n)
{
if (!m_optionWidget) // widget will not exist if the are no documents open
return;
KisSignalsBlocker blocker(m_optionWidget);
qreal opacity = m_sliderChooser[n]->getWidget("opacity")->value();
qreal flow = m_sliderChooser[n]->getWidget("flow")->value();
qreal size = m_sliderChooser[n]->getWidget("size")->value();
setSliderValue("opacity", opacity);
setSliderValue("flow" , flow);
setSliderValue("size" , size);
if (m_presetsEnabled) {
// IMPORTANT: set the PaintOp size before setting the other properties
// it wont work the other way
// TODO: why?!
m_resourceProvider->setSize(size);
m_resourceProvider->setOpacity(opacity);
m_resourceProvider->setFlow(flow);
KisLockedPropertiesProxySP propertiesProxy = KisLockedPropertiesServer::instance()->createLockedPropertiesProxy(m_resourceProvider->currentPreset()->settings());
propertiesProxy->setProperty("OpacityValue", opacity);
propertiesProxy->setProperty("FlowValue", flow);
m_optionWidget->setConfigurationSafe(m_resourceProvider->currentPreset()->settings().data());
} else {
m_resourceProvider->setOpacity(opacity);
}
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
}
void KisPaintopBox::slotSlider1Changed()
{
sliderChanged(0);
}
void KisPaintopBox::slotSlider2Changed()
{
sliderChanged(1);
}
void KisPaintopBox::slotSlider3Changed()
{
sliderChanged(2);
}
void KisPaintopBox::slotToolChanged(KoCanvasController* canvas, int toolId)
{
Q_UNUSED(canvas);
Q_UNUSED(toolId);
if (!m_viewManager->canvasBase()) return;
QString id = KoToolManager::instance()->activeToolId();
KisTool* tool = dynamic_cast(KoToolManager::instance()->toolById(m_viewManager->canvasBase(), id));
if (tool) {
int flags = tool->flags();
if (flags & KisTool::FLAG_USES_CUSTOM_COMPOSITEOP) {
setWidgetState(ENABLE_COMPOSITEOP | ENABLE_OPACITY);
} else {
setWidgetState(DISABLE_COMPOSITEOP | DISABLE_OPACITY);
}
if (flags & KisTool::FLAG_USES_CUSTOM_PRESET) {
setWidgetState(ENABLE_PRESETS);
slotUpdatePreset();
m_presetsEnabled = true;
} else {
setWidgetState(DISABLE_PRESETS);
m_presetsEnabled = false;
}
if (flags & KisTool::FLAG_USES_CUSTOM_SIZE) {
setWidgetState(ENABLE_SIZE | ENABLE_FLOW);
} else {
setWidgetState(DISABLE_SIZE | DISABLE_FLOW);
}
} else setWidgetState(DISABLE_ALL);
}
void KisPaintopBox::slotPreviousFavoritePreset()
{
if (!m_favoriteResourceManager) return;
- int i = 0;
- Q_FOREACH (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) {
- if (m_resourceProvider->currentPreset() && m_resourceProvider->currentPreset()->name() == preset->name()) {
+ QVector presets = m_favoriteResourceManager->favoritePresetList();
+ for (int i=0; i < presets.size(); ++i) {
+ if (m_resourceProvider->currentPreset() &&
+ m_resourceProvider->currentPreset()->name() == presets[i]->name()) {
if (i > 0) {
m_favoriteResourceManager->slotChangeActivePaintop(i - 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(m_favoriteResourceManager->numFavoritePresets() - 1);
}
+ //floating message should have least 2 lines, otherwise
+ //preset thumbnail will be too small to distinguish
+ //(because size of image on floating message depends on amount of lines in msg)
+ m_viewManager->showFloatingMessage(
+ i18n("%1\nselected",
+ m_resourceProvider->currentPreset()->name()),
+ QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
+
return;
}
- i++;
}
-
}
void KisPaintopBox::slotNextFavoritePreset()
{
if (!m_favoriteResourceManager) return;
- int i = 0;
- Q_FOREACH (KisPaintOpPresetSP preset, m_favoriteResourceManager->favoritePresetList()) {
- if (m_resourceProvider->currentPreset()->name() == preset->name()) {
+ QVector presets = m_favoriteResourceManager->favoritePresetList();
+ for(int i = 0; i < presets.size(); ++i) {
+ if (m_resourceProvider->currentPreset()->name() == presets[i]->name()) {
if (i < m_favoriteResourceManager->numFavoritePresets() - 1) {
m_favoriteResourceManager->slotChangeActivePaintop(i + 1);
} else {
m_favoriteResourceManager->slotChangeActivePaintop(0);
}
+ m_viewManager->showFloatingMessage(
+ i18n("%1\nselected",
+ m_resourceProvider->currentPreset()->name()),
+ QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
+
return;
}
- i++;
}
}
void KisPaintopBox::slotSwitchToPreviousPreset()
{
if (m_resourceProvider->previousPreset()) {
//qDebug() << "slotSwitchToPreviousPreset();" << m_resourceProvider->previousPreset();
setCurrentPaintop(m_resourceProvider->previousPreset());
+ m_viewManager->showFloatingMessage(
+ i18n("%1\nselected",
+ m_resourceProvider->currentPreset()->name()),
+ QIcon(QPixmap::fromImage(m_resourceProvider->currentPreset()->image())));
}
}
void KisPaintopBox::slotUnsetEraseMode()
{
m_eraseAction->setChecked(false);
}
void KisPaintopBox::slotToggleAlphaLockMode(bool checked)
{
if (checked) {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-locked"));
} else {
m_alphaLockButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transparency-unlocked"));
}
m_resourceProvider->setGlobalAlphaLock(checked);
}
void KisPaintopBox::slotDisablePressureMode(bool checked)
{
if (checked) {
m_disablePressureButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
} else {
m_disablePressureButton->actions()[0]->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
}
m_resourceProvider->setDisablePressure(checked);
}
void KisPaintopBox::slotReloadPreset()
{
KisSignalsBlocker blocker(m_optionWidget);
//Here using the name and fetching the preset from the server was the only way the load was working. Otherwise it was not loading.
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
KisPaintOpPresetSP preset = rserver->resourceByName(m_resourceProvider->currentPreset()->name());
if (preset) {
preset->load();
}
}
void KisPaintopBox::slotGuiChangedCurrentPreset() // Called only when UI is changed and not when preset is changed
{
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
{
/**
* Here we postpone all the settings updates events until thye entire writing
* operation will be finished. As soon as it is finished, the updates will be
* emitted happily (if there were any).
*/
KisPaintOpPreset::UpdatedPostponer postponer(preset.data());
m_optionWidget->writeConfigurationSafe(const_cast(preset->settings().data()));
}
// we should also update the preset strip to update the status of the "dirty" mark
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
// TODO!!!!!!!!
//m_presetsPopup->updateViewSettings();
}
void KisPaintopBox::slotSaveLockedOptionToPreset(KisPropertiesConfigurationSP p)
{
QMapIterator i(p->getProperties());
while (i.hasNext()) {
i.next();
m_resourceProvider->currentPreset()->settings()->setProperty(i.key(), QVariant(i.value()));
if (m_resourceProvider->currentPreset()->settings()->hasProperty(i.key() + "_previous")) {
m_resourceProvider->currentPreset()->settings()->removeProperty(i.key() + "_previous");
}
}
slotGuiChangedCurrentPreset();
}
void KisPaintopBox::slotDropLockedOption(KisPropertiesConfigurationSP p)
{
KisSignalsBlocker blocker(m_optionWidget);
KisPaintOpPresetSP preset = m_resourceProvider->currentPreset();
{
KisPaintOpPreset::DirtyStateSaver dirtySaver(preset.data());
QMapIterator i(p->getProperties());
while (i.hasNext()) {
i.next();
if (preset->settings()->hasProperty(i.key() + "_previous")) {
preset->settings()->setProperty(i.key(), preset->settings()->getProperty(i.key() + "_previous"));
preset->settings()->removeProperty(i.key() + "_previous");
}
}
}
//slotUpdatePreset();
}
void KisPaintopBox::slotDirtyPresetToggled(bool value)
{
if (!value) {
slotReloadPreset();
m_presetsPopup->resourceSelected(m_resourceProvider->currentPreset().data());
m_presetsPopup->updateViewSettings();
}
m_dirtyPresetsEnabled = value;
KisConfig cfg;
cfg.setUseDirtyPresets(m_dirtyPresetsEnabled);
}
void KisPaintopBox::slotEraserBrushSizeToggled(bool value)
{
m_eraserBrushSizeEnabled = value;
KisConfig cfg;
cfg.setUseEraserBrushSize(m_eraserBrushSizeEnabled);
}
void KisPaintopBox::slotEraserBrushOpacityToggled(bool value)
{
m_eraserBrushOpacityEnabled = value;
KisConfig cfg;
cfg.setUseEraserBrushOpacity(m_eraserBrushOpacityEnabled);
}
void KisPaintopBox::slotUpdateSelectionIcon()
{
m_hMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-horizontal"));
m_vMirrorAction->setIcon(KisIconUtils::loadIcon("symmetry-vertical"));
KisConfig cfg;
if (!cfg.toolOptionsInDocker() && m_toolOptionsPopupButton) {
m_toolOptionsPopupButton->setIcon(KisIconUtils::loadIcon("configure"));
}
m_presetSelectorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_01"));
m_brushEditorPopupButton->setIcon(KisIconUtils::loadIcon("paintop_settings_02"));
m_workspaceWidget->setIcon(KisIconUtils::loadIcon("view-choose"));
m_eraseAction->setIcon(KisIconUtils::loadIcon("draw-eraser"));
m_reloadAction->setIcon(KisIconUtils::loadIcon("view-refresh"));
if (m_disablePressureAction->isChecked()) {
m_disablePressureButton->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure"));
} else {
m_disablePressureButton->setIcon(KisIconUtils::loadIcon("transform_icons_penPressure_locked"));
}
}
void KisPaintopBox::slotLockXMirrorToggle(bool toggleLock) {
m_resourceProvider->setMirrorHorizontalLock(toggleLock);
}
void KisPaintopBox::slotLockYMirrorToggle(bool toggleLock) {
m_resourceProvider->setMirrorVerticalLock(toggleLock);
}
void KisPaintopBox::slotHideDecorationMirrorX(bool toggled) {
m_resourceProvider->setMirrorHorizontalHideDecorations(toggled);
}
void KisPaintopBox::slotHideDecorationMirrorY(bool toggled) {
m_resourceProvider->setMirrorVerticalHideDecorations(toggled);
}
void KisPaintopBox::slotMoveToCenterMirrorX() {
m_resourceProvider->mirrorHorizontalMoveCanvasToCenter();
}
void KisPaintopBox::slotMoveToCenterMirrorY() {
m_resourceProvider->mirrorVerticalMoveCanvasToCenter();
}
diff --git a/libs/ui/opengl/kis_opengl_canvas2.cpp b/libs/ui/opengl/kis_opengl_canvas2.cpp
index 4c05ba1a37..6cae9872af 100644
--- a/libs/ui/opengl/kis_opengl_canvas2.cpp
+++ b/libs/ui/opengl/kis_opengl_canvas2.cpp
@@ -1,889 +1,894 @@
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt , (C) 2006-2013
* Copyright (C) 2015 Michael Abrahams
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define GL_GLEXT_PROTOTYPES
#include "opengl/kis_opengl_canvas2.h"
#include "opengl/kis_opengl_canvas2_p.h"
#include "opengl/kis_opengl_shader_loader.h"
#include "opengl/kis_opengl_canvas_debugger.h"
#include "canvas/kis_canvas2.h"
#include "canvas/kis_coordinates_converter.h"
#include "canvas/kis_display_filter.h"
#include "canvas/kis_display_color_converter.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef Q_OS_OSX
#include
#endif
#define NEAR_VAL -1000.0
#define FAR_VAL 1000.0
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif
#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
static bool OPENGL_SUCCESS = false;
struct KisOpenGLCanvas2::Private
{
public:
~Private() {
delete displayShader;
delete checkerShader;
delete solidColorShader;
Sync::deleteSync(glSyncObject);
}
bool canvasInitialized{false};
KisOpenGLImageTexturesSP openGLImageTextures;
KisOpenGLShaderLoader shaderLoader;
KisShaderProgram *displayShader{0};
KisShaderProgram *checkerShader{0};
KisShaderProgram *solidColorShader{0};
bool displayShaderCompiledWithDisplayFilterSupport{false};
GLfloat checkSizeScale;
bool scrollCheckers;
QSharedPointer displayFilter;
KisOpenGL::FilterMode filterMode;
bool proofingConfigIsUpdated=false;
GLsync glSyncObject{0};
bool wrapAroundMode{false};
// Stores a quad for drawing the canvas
QOpenGLVertexArrayObject quadVAO;
QOpenGLBuffer quadBuffers[2];
// Stores data for drawing tool outlines
QOpenGLVertexArrayObject outlineVAO;
QOpenGLBuffer lineBuffer;
QVector3D vertices[6];
QVector2D texCoords[6];
#ifndef Q_OS_OSX
QOpenGLFunctions_2_1 *glFn201;
#endif
+ qreal pixelGridDrawingThreshold;
+ bool pixelGridEnabled;
+ QColor gridColor;
+
int xToColWithWrapCompensation(int x, const QRect &imageRect) {
int firstImageColumn = openGLImageTextures->xToCol(imageRect.left());
int lastImageColumn = openGLImageTextures->xToCol(imageRect.right());
int colsPerImage = lastImageColumn - firstImageColumn + 1;
int numWraps = floor(qreal(x) / imageRect.width());
int remainder = x - imageRect.width() * numWraps;
return colsPerImage * numWraps + openGLImageTextures->xToCol(remainder);
}
int yToRowWithWrapCompensation(int y, const QRect &imageRect) {
int firstImageRow = openGLImageTextures->yToRow(imageRect.top());
int lastImageRow = openGLImageTextures->yToRow(imageRect.bottom());
int rowsPerImage = lastImageRow - firstImageRow + 1;
int numWraps = floor(qreal(y) / imageRect.height());
int remainder = y - imageRect.height() * numWraps;
return rowsPerImage * numWraps + openGLImageTextures->yToRow(remainder);
}
};
KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas,
KisCoordinatesConverter *coordinatesConverter,
QWidget *parent,
KisImageWSP image,
KisDisplayColorConverter *colorConverter)
: QOpenGLWidget(parent)
, KisCanvasWidgetBase(canvas, coordinatesConverter)
, d(new Private())
{
KisConfig cfg;
cfg.setCanvasState("OPENGL_STARTED");
d->openGLImageTextures =
KisOpenGLImageTextures::getImageTextures(image,
colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
setAcceptDrops(true);
setAutoFillBackground(false);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_NoSystemBackground, true);
#ifdef Q_OS_OSX
setAttribute(Qt::WA_AcceptTouchEvents, false);
#else
setAttribute(Qt::WA_AcceptTouchEvents, true);
#endif
setAttribute(Qt::WA_InputMethodEnabled, true);
setAttribute(Qt::WA_DontCreateNativeAncestors, true);
setDisplayFilterImpl(colorConverter->displayFilter(), true);
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
}
KisOpenGLCanvas2::~KisOpenGLCanvas2()
{
delete d;
}
bool KisOpenGLCanvas2::needsFpsDebugging() const
{
return KisOpenglCanvasDebugger::instance()->showFpsOnCanvas();
}
void KisOpenGLCanvas2::setDisplayFilter(QSharedPointer displayFilter)
{
setDisplayFilterImpl(displayFilter, false);
}
void KisOpenGLCanvas2::setDisplayFilterImpl(QSharedPointer displayFilter, bool initializing)
{
bool needsInternalColorManagement =
!displayFilter || displayFilter->useInternalColorManagement();
bool needsFullRefresh = d->openGLImageTextures->setInternalColorManagementActive(needsInternalColorManagement);
d->displayFilter = displayFilter;
if (!initializing && needsFullRefresh) {
canvas()->startUpdateInPatches(canvas()->image()->bounds());
}
else if (!initializing) {
canvas()->updateCanvas();
}
}
void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value)
{
d->wrapAroundMode = value;
update();
}
inline void rectToVertices(QVector3D* vertices, const QRectF &rc)
{
vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f);
vertices[1] = QVector3D(rc.left(), rc.top(), 0.f);
vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f);
vertices[3] = QVector3D(rc.left(), rc.top(), 0.f);
vertices[4] = QVector3D(rc.right(), rc.top(), 0.f);
vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f);
}
inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc)
{
texCoords[0] = QVector2D(rc.left(), rc.bottom());
texCoords[1] = QVector2D(rc.left(), rc.top());
texCoords[2] = QVector2D(rc.right(), rc.bottom());
texCoords[3] = QVector2D(rc.left(), rc.top());
texCoords[4] = QVector2D(rc.right(), rc.top());
texCoords[5] = QVector2D(rc.right(), rc.bottom());
}
void KisOpenGLCanvas2::initializeGL()
{
KisOpenGL::initializeContext(context());
initializeOpenGLFunctions();
#ifndef Q_OS_OSX
d->glFn201 = context()->versionFunctions();
if (!d->glFn201) {
warnUI << "Cannot obtain QOpenGLFunctions_2_1, glLogicOp cannot be used";
}
#endif
KisConfig cfg;
d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration());
d->openGLImageTextures->initGL(context()->functions());
d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize()));
initializeShaders();
// If we support OpenGL 3.2, then prepare our VAOs and VBOs for drawing
if (KisOpenGL::hasOpenGL3()) {
d->quadVAO.create();
d->quadVAO.bind();
glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE);
glEnableVertexAttribArray(PROGRAM_TEXCOORD_ATTRIBUTE);
// Create the vertex buffer object, it has 6 vertices with 3 components
d->quadBuffers[0].create();
d->quadBuffers[0].setUsagePattern(QOpenGLBuffer::StaticDraw);
d->quadBuffers[0].bind();
d->quadBuffers[0].allocate(d->vertices, 6 * 3 * sizeof(float));
glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0);
// Create the texture buffer object, it has 6 texture coordinates with 2 components
d->quadBuffers[1].create();
d->quadBuffers[1].setUsagePattern(QOpenGLBuffer::StaticDraw);
d->quadBuffers[1].bind();
d->quadBuffers[1].allocate(d->texCoords, 6 * 2 * sizeof(float));
glVertexAttribPointer(PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0);
// Create the outline buffer, this buffer will store the outlines of
// tools and will frequently change data
d->outlineVAO.create();
d->outlineVAO.bind();
glEnableVertexAttribArray(PROGRAM_VERTEX_ATTRIBUTE);
// The outline buffer has a StreamDraw usage pattern, because it changes constantly
d->lineBuffer.create();
d->lineBuffer.setUsagePattern(QOpenGLBuffer::StreamDraw);
d->lineBuffer.bind();
glVertexAttribPointer(PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0, 0);
}
Sync::init(context());
d->canvasInitialized = true;
}
/**
* Loads all shaders and reports compilation problems
*/
void KisOpenGLCanvas2::initializeShaders()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!d->canvasInitialized);
delete d->checkerShader;
delete d->solidColorShader;
d->checkerShader = 0;
d->solidColorShader = 0;
try {
d->checkerShader = d->shaderLoader.loadCheckerShader();
d->solidColorShader = d->shaderLoader.loadSolidColorShader();
} catch (const ShaderLoaderException &e) {
reportFailedShaderCompilation(e.what());
}
initializeDisplayShader();
}
void KisOpenGLCanvas2::initializeDisplayShader()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!d->canvasInitialized);
bool useHiQualityFiltering = d->filterMode == KisOpenGL::HighQualityFiltering;
delete d->displayShader;
d->displayShader = 0;
try {
d->displayShader = d->shaderLoader.loadDisplayShader(d->displayFilter, useHiQualityFiltering);
d->displayShaderCompiledWithDisplayFilterSupport = d->displayFilter;
} catch (const ShaderLoaderException &e) {
reportFailedShaderCompilation(e.what());
}
}
/**
* Displays a message box telling the user that
* shader compilation failed and turns off OpenGL.
*/
void KisOpenGLCanvas2::reportFailedShaderCompilation(const QString &context)
{
KisConfig cfg;
qDebug() << "Shader Compilation Failure: " << context;
QMessageBox::critical(this, i18nc("@title:window", "Krita"),
QString(i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n Krita will disable OpenGL and close now.")).arg(context),
QMessageBox::Close);
cfg.setUseOpenGL(false);
cfg.setCanvasState("OPENGL_FAILED");
}
void KisOpenGLCanvas2::resizeGL(int width, int height)
{
coordinatesConverter()->setCanvasWidgetSize(QSize(width, height));
paintGL();
}
void KisOpenGLCanvas2::paintGL()
{
if (!OPENGL_SUCCESS) {
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED");
}
KisOpenglCanvasDebugger::instance()->nofityPaintRequested();
renderCanvasGL();
if (d->glSyncObject) {
Sync::deleteSync(d->glSyncObject);
}
d->glSyncObject = Sync::getSync();
QPainter gc(this);
renderDecorations(&gc);
gc.end();
if (!OPENGL_SUCCESS) {
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
OPENGL_SUCCESS = true;
}
}
void KisOpenGLCanvas2::paintToolOutline(const QPainterPath &path)
{
if (!d->solidColorShader->bind()) {
return;
}
// setup the mvp transformation
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(coordinatesConverter()->flakeToWidgetTransform());
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->solidColorShader->setUniformValue(d->solidColorShader->location(Uniform::ModelViewProjection), modelMatrix);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable(GL_COLOR_LOGIC_OP);
#ifndef Q_OS_OSX
if (d->glFn201) {
d->glFn201->glLogicOp(GL_XOR);
}
#else
glLogicOp(GL_XOR);
#endif
KisConfig cfg;
QColor cursorColor = cfg.getCursorMainColor();
d->solidColorShader->setUniformValue(
d->solidColorShader->location(Uniform::FragmentColor),
QVector4D(cursorColor.redF(), cursorColor.greenF(), cursorColor.blueF(), 1.0f));
// Paint the tool outline
if (KisOpenGL::hasOpenGL3()) {
d->outlineVAO.bind();
d->lineBuffer.bind();
}
// Convert every disjointed subpath to a polygon and draw that polygon
QList subPathPolygons = path.toSubpathPolygons();
for (int i = 0; i < subPathPolygons.size(); i++) {
const QPolygonF& polygon = subPathPolygons.at(i);
QVector vertices;
vertices.resize(polygon.count());
for (int j = 0; j < polygon.count(); j++) {
QPointF p = polygon.at(j);
vertices[j].setX(p.x());
vertices[j].setY(p.y());
}
if (KisOpenGL::hasOpenGL3()) {
d->lineBuffer.allocate(vertices.constData(), 3 * vertices.size() * sizeof(float));
}
else {
d->solidColorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->solidColorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, vertices.constData());
}
glDrawArrays(GL_LINE_STRIP, 0, vertices.size());
}
if (KisOpenGL::hasOpenGL3()) {
d->lineBuffer.release();
d->outlineVAO.release();
}
glDisable(GL_COLOR_LOGIC_OP);
d->solidColorShader->release();
}
bool KisOpenGLCanvas2::isBusy() const
{
const bool isBusyStatus = Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled;
KisOpenglCanvasDebugger::instance()->nofitySyncStatus(isBusyStatus);
return isBusyStatus;
}
void KisOpenGLCanvas2::drawCheckers()
{
if (!d->checkerShader) {
return;
}
KisCoordinatesConverter *converter = coordinatesConverter();
QTransform textureTransform;
QTransform modelTransform;
QRectF textureRect;
QRectF modelRect;
QRectF viewportRect = !d->wrapAroundMode ?
converter->imageRectInViewportPixels() :
converter->widgetToViewport(this->rect());
converter->getOpenGLCheckersInfo(viewportRect,
&textureTransform, &modelTransform, &textureRect, &modelRect, d->scrollCheckers);
textureTransform *= QTransform::fromScale(d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
d->checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);
if (!d->checkerShader->bind()) {
qWarning() << "Could not bind checker shader";
return;
}
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(modelTransform);
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->checkerShader->setUniformValue(d->checkerShader->location(Uniform::ModelViewProjection), modelMatrix);
QMatrix4x4 textureMatrix(textureTransform);
d->checkerShader->setUniformValue(d->checkerShader->location(Uniform::TextureMatrix), textureMatrix);
//Setup the geometry for rendering
if (KisOpenGL::hasOpenGL3()) {
rectToVertices(d->vertices, modelRect);
d->quadBuffers[0].bind();
d->quadBuffers[0].write(0, d->vertices, 3 * 6 * sizeof(float));
rectToTexCoords(d->texCoords, textureRect);
d->quadBuffers[1].bind();
d->quadBuffers[1].write(0, d->texCoords, 2 * 6 * sizeof(float));
}
else {
rectToVertices(d->vertices, modelRect);
d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices);
rectToTexCoords(d->texCoords, textureRect);
d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords);
}
// render checkers
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, d->openGLImageTextures->checkerTexture());
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindTexture(GL_TEXTURE_2D, 0);
d->checkerShader->release();
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void KisOpenGLCanvas2::drawGrid()
{
if (!d->solidColorShader->bind()) {
return;
}
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(coordinatesConverter()->imageToWidgetTransform());
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->solidColorShader->setUniformValue(d->solidColorShader->location(Uniform::ModelViewProjection), modelMatrix);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- KisConfig cfg;
- QColor gridColor = cfg.getPixelGridColor();
d->solidColorShader->setUniformValue(
d->solidColorShader->location(Uniform::FragmentColor),
- QVector4D(gridColor.redF(), gridColor.greenF(), gridColor.blueF(), 0.5f));
+ QVector4D(d->gridColor.redF(), d->gridColor.greenF(), d->gridColor.blueF(), 0.5f));
if (KisOpenGL::hasOpenGL3()) {
d->outlineVAO.bind();
d->lineBuffer.bind();
}
QRectF widgetRect(0,0, width(), height());
QRectF widgetRectInImagePixels = coordinatesConverter()->documentToImage(coordinatesConverter()->widgetToDocument(widgetRect));
QRect wr = widgetRectInImagePixels.toAlignedRect();
if (!d->wrapAroundMode) {
wr &= d->openGLImageTextures->storedImageBounds();
}
QPoint topLeftCorner = wr.topLeft();
QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1);
QVector grid;
for (int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) {
grid.append(QVector3D(i, topLeftCorner.y(), 0));
grid.append(QVector3D(i, bottomRightCorner.y(), 0));
}
for (int i = topLeftCorner.y(); i <= bottomRightCorner.y(); ++i) {
grid.append(QVector3D(topLeftCorner.x(), i, 0));
grid.append(QVector3D(bottomRightCorner.x(), i, 0));
}
if (KisOpenGL::hasOpenGL3()) {
d->lineBuffer.allocate(grid.constData(), 3 * grid.size() * sizeof(float));
}
else {
d->solidColorShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->solidColorShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, grid.constData());
}
glDrawArrays(GL_LINES, 0, grid.size());
if (KisOpenGL::hasOpenGL3()) {
d->lineBuffer.release();
d->outlineVAO.release();
}
d->solidColorShader->release();
}
void KisOpenGLCanvas2::drawImage()
{
if (!d->displayShader) {
return;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
KisCoordinatesConverter *converter = coordinatesConverter();
d->displayShader->bind();
QMatrix4x4 projectionMatrix;
projectionMatrix.setToIdentity();
projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
// Set view/projection matrices
QMatrix4x4 modelMatrix(converter->imageToWidgetTransform());
modelMatrix.optimize();
modelMatrix = projectionMatrix * modelMatrix;
d->displayShader->setUniformValue(d->displayShader->location(Uniform::ModelViewProjection), modelMatrix);
QMatrix4x4 textureMatrix;
textureMatrix.setToIdentity();
d->displayShader->setUniformValue(d->displayShader->location(Uniform::TextureMatrix), textureMatrix);
QRectF widgetRect(0,0, width(), height());
QRectF widgetRectInImagePixels = converter->documentToImage(converter->widgetToDocument(widgetRect));
qreal scaleX, scaleY;
converter->imageScale(&scaleX, &scaleY);
d->displayShader->setUniformValue(d->displayShader->location(Uniform::ViewportScale), (GLfloat) scaleX);
d->displayShader->setUniformValue(d->displayShader->location(Uniform::TexelSize), (GLfloat) d->openGLImageTextures->texelSize());
QRect ir = d->openGLImageTextures->storedImageBounds();
QRect wr = widgetRectInImagePixels.toAlignedRect();
if (!d->wrapAroundMode) {
// if we don't want to paint wrapping images, just limit the
// processing area, and the code will handle all the rest
wr &= ir;
}
int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir);
int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir);
int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir);
int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir);
int minColumn = d->openGLImageTextures->xToCol(ir.left());
int maxColumn = d->openGLImageTextures->xToCol(ir.right());
int minRow = d->openGLImageTextures->yToRow(ir.top());
int maxRow = d->openGLImageTextures->yToRow(ir.bottom());
int imageColumns = maxColumn - minColumn + 1;
int imageRows = maxRow - minRow + 1;
for (int col = firstColumn; col <= lastColumn; col++) {
for (int row = firstRow; row <= lastRow; row++) {
int effectiveCol = col;
int effectiveRow = row;
QPointF tileWrappingTranslation;
if (effectiveCol > maxColumn || effectiveCol < minColumn) {
int translationStep = floor(qreal(col) / imageColumns);
int originCol = translationStep * imageColumns;
effectiveCol = col - originCol;
tileWrappingTranslation.rx() = translationStep * ir.width();
}
if (effectiveRow > maxRow || effectiveRow < minRow) {
int translationStep = floor(qreal(row) / imageRows);
int originRow = translationStep * imageRows;
effectiveRow = row - originRow;
tileWrappingTranslation.ry() = translationStep * ir.height();
}
KisTextureTile *tile =
d->openGLImageTextures->getTextureTileCR(effectiveCol, effectiveRow);
if (!tile) {
warnUI << "OpenGL: Trying to paint texture tile but it has not been created yet.";
continue;
}
/*
* We create a float rect here to workaround Qt's
* "history reasons" in calculation of right()
* and bottom() coordinates of integer rects.
*/
QRectF textureRect(tile->tileRectInTexturePixels());
QRectF modelRect(tile->tileRectInImagePixels().translated(tileWrappingTranslation.x(), tileWrappingTranslation.y()));
//Setup the geometry for rendering
if (KisOpenGL::hasOpenGL3()) {
rectToVertices(d->vertices, modelRect);
d->quadBuffers[0].bind();
d->quadBuffers[0].write(0, d->vertices, 3 * 6 * sizeof(float));
rectToTexCoords(d->texCoords, textureRect);
d->quadBuffers[1].bind();
d->quadBuffers[1].write(0, d->texCoords, 2 * 6 * sizeof(float));
}
else {
rectToVertices(d->vertices, modelRect);
d->displayShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
d->displayShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices);
rectToTexCoords(d->texCoords, textureRect);
d->displayShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
d->displayShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords);
}
if (d->displayFilter) {
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_3D, d->displayFilter->lutTexture());
d->displayShader->setUniformValue(d->displayShader->location(Uniform::Texture1), 1);
}
int currentLodPlane = tile->currentLodPlane();
if (d->displayShader->location(Uniform::FixedLodLevel) >= 0) {
d->displayShader->setUniformValue(d->displayShader->location(Uniform::FixedLodLevel),
(GLfloat) currentLodPlane);
}
glActiveTexture(GL_TEXTURE0);
tile->bindToActiveTexture();
if (currentLodPlane > 0) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
} else if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 2.0)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
switch(d->filterMode) {
case KisOpenGL::NearestFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case KisOpenGL::BilinearFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case KisOpenGL::TrilinearFilterMode:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case KisOpenGL::HighQualityFiltering:
if (SCALE_LESS_THAN(scaleX, scaleY, 0.5)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
break;
}
}
glDrawArrays(GL_TRIANGLES, 0, 6);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
d->displayShader->release();
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void KisOpenGLCanvas2::slotConfigChanged()
{
KisConfig cfg;
d->checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast(cfg.checkSize());
d->scrollCheckers = cfg.scrollCheckers();
d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize()));
d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels());
d->filterMode = (KisOpenGL::FilterMode) cfg.openGLFilteringMode();
+ d->pixelGridDrawingThreshold = cfg.getPixelGridDrawingThreshold();
+ d->pixelGridEnabled = cfg.pixelGridEnabled();
+ d->gridColor = cfg.getPixelGridColor();
+
notifyConfigChanged();
}
QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const
{
return processInputMethodQuery(query);
}
void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event)
{
processInputMethodEvent(event);
}
void KisOpenGLCanvas2::renderCanvasGL()
{
// Draw the border (that is, clear the whole widget to the border color)
QColor widgetBackgroundColor = borderColor();
glClearColor(widgetBackgroundColor.redF(), widgetBackgroundColor.greenF(), widgetBackgroundColor.blueF(), 1.0);
glClear(GL_COLOR_BUFFER_BIT);
if ((d->displayFilter && d->displayFilter->updateShader()) ||
(bool(d->displayFilter) != d->displayShaderCompiledWithDisplayFilterSupport)) {
KIS_SAFE_ASSERT_RECOVER_NOOP(d->canvasInitialized);
d->canvasInitialized = false; // TODO: check if actually needed?
initializeDisplayShader();
d->canvasInitialized = true;
}
if (KisOpenGL::hasOpenGL3()) {
d->quadVAO.bind();
}
drawCheckers();
drawImage();
- KisConfig cfg;
- if ((coordinatesConverter()->effectiveZoom() > cfg.getPixelGridDrawingThreshold() - 0.00001) && cfg.pixelGridEnabled()) {
+ if ((coordinatesConverter()->effectiveZoom() > d->pixelGridDrawingThreshold - 0.00001) && d->pixelGridEnabled) {
drawGrid();
}
if (KisOpenGL::hasOpenGL3()) {
d->quadVAO.release();
}
}
void KisOpenGLCanvas2::renderDecorations(QPainter *painter)
{
QRect boundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect();
drawDecorations(*painter, boundingRect);
}
void KisOpenGLCanvas2::setDisplayProfile(KisDisplayColorConverter *colorConverter)
{
d->openGLImageTextures->setMonitorProfile(colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
}
void KisOpenGLCanvas2::channelSelectionChanged(const QBitArray &channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
}
void KisOpenGLCanvas2::finishResizingImage(qint32 w, qint32 h)
{
if (d->canvasInitialized) {
d->openGLImageTextures->slotImageSizeChanged(w, h);
}
}
KisUpdateInfoSP KisOpenGLCanvas2::startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
if (canvas()->proofingConfigUpdated()) {
d->openGLImageTextures->setProofingConfig(canvas()->proofingConfiguration());
canvas()->setProofingConfigUpdated(false);
}
return d->openGLImageTextures->updateCache(rc);
}
QRect KisOpenGLCanvas2::updateCanvasProjection(KisUpdateInfoSP info)
{
// See KisQPainterCanvas::updateCanvasProjection for more info
bool isOpenGLUpdateInfo = dynamic_cast(info.data());
if (isOpenGLUpdateInfo) {
d->openGLImageTextures->recalculateCache(info);
}
#ifdef Q_OS_OSX
/**
* There is a bug on OSX: if we issue frame redraw before the tiles finished
* uploading, the tiles will become corrupted. Depending on the GPU/driver
* version either the tile itself, or its mipmaps will become totally
* transparent.
*/
glFinish();
#endif
return QRect(); // FIXME: Implement dirty rect for OpenGL
}
bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next)
{
return focusNextPrevChild(next);
}
KisOpenGLImageTexturesSP KisOpenGLCanvas2::openGLImageTextures() const
{
return d->openGLImageTextures;
}
diff --git a/libs/ui/widgets/KisVisualColorSelectorShape.cpp b/libs/ui/widgets/KisVisualColorSelectorShape.cpp
index 104d39a4f3..cfdda136c4 100644
--- a/libs/ui/widgets/KisVisualColorSelectorShape.cpp
+++ b/libs/ui/widgets/KisVisualColorSelectorShape.cpp
@@ -1,604 +1,620 @@
/*
* Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "KisVisualColorSelectorShape.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KoColorConversions.h"
#include "KoColorDisplayRendererInterface.h"
#include "KoChannelInfo.h"
#include
#include
#include "kis_signal_compressor.h"
#include "kis_debug.h"
struct KisVisualColorSelectorShape::Private
{
QImage gradient;
QImage fullSelector;
bool imagesNeedUpdate {true};
QPointF currentCoordinates;
Dimensions dimension;
ColorModel model;
const KoColorSpace *colorSpace;
KoColor currentColor;
int channel1;
int channel2;
KisSignalCompressor *updateTimer {0};
bool mousePressActive = false;
const KoColorDisplayRendererInterface *displayRenderer = 0;
qreal hue = 0.0;
qreal sat = 0.0;
qreal tone = 0.0;
+ bool usesOCIO = false;
+ bool isRGBA = false;
+ bool is8Bit = false;
};
KisVisualColorSelectorShape::KisVisualColorSelectorShape(QWidget *parent,
KisVisualColorSelectorShape::Dimensions dimension,
KisVisualColorSelectorShape::ColorModel model,
const KoColorSpace *cs,
int channel1,
int channel2,
const KoColorDisplayRendererInterface *displayRenderer): QWidget(parent), m_d(new Private)
{
m_d->dimension = dimension;
m_d->model = model;
m_d->colorSpace = cs;
+
+ // TODO: The following is done because the IDs are actually strings. Ideally, in the future, we
+ // refactor everything so that the IDs are actually proper enums or something faster.
+ if (m_d->displayRenderer
+ && (m_d->colorSpace->colorDepthId() == Float16BitsColorDepthID
+ || m_d->colorSpace->colorDepthId() == Float32BitsColorDepthID
+ || m_d->colorSpace->colorDepthId() == Float64BitsColorDepthID)
+ && m_d->colorSpace->colorModelId() != LABAColorModelID
+ && m_d->colorSpace->colorModelId() != CMYKAColorModelID) {
+ m_d->usesOCIO = true;
+ } else {
+ m_d->usesOCIO = false;
+ }
+ if (m_d->colorSpace->colorModelId() == RGBAColorModelID) {
+ m_d->isRGBA = true;
+ } else {
+ m_d->isRGBA = false;
+ }
+ if (m_d->colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
+ m_d->is8Bit = true;
+ } else {
+ m_d->is8Bit = false;
+ }
m_d->currentColor = KoColor();
m_d->currentColor.setOpacity(1.0);
m_d->currentColor.convertTo(cs);
int maxchannel = m_d->colorSpace->colorChannelCount()-1;
m_d->channel1 = qBound(0, channel1, maxchannel);
m_d->channel2 = qBound(0, channel2, maxchannel);
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
// HACK: the updateTimer isn't connected to anything, we only check whether it's still active
// and running in order to determine whether we will emit a certain signal.
m_d->updateTimer = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this);
setDisplayRenderer(displayRenderer);
show();
}
KisVisualColorSelectorShape::~KisVisualColorSelectorShape()
{
}
void KisVisualColorSelectorShape::updateCursor()
{
QPointF point1 = convertKoColorToShapeCoordinate(m_d->currentColor);
if (point1 != m_d->currentCoordinates) {
m_d->currentCoordinates = point1;
}
}
QPointF KisVisualColorSelectorShape::getCursorPosition() {
return m_d->currentCoordinates;
}
void KisVisualColorSelectorShape::setColor(KoColor c)
{
//qDebug() << this << "KisVisualColorSelectorShape::setColor";
if (c.colorSpace() != m_d->colorSpace) {
c.convertTo(m_d->colorSpace);
}
m_d->currentColor = c;
updateCursor();
m_d->imagesNeedUpdate = true;
update();
}
void KisVisualColorSelectorShape::setColorFromSibling(KoColor c)
{
//qDebug() << this << "setColorFromSibling";
if (c.colorSpace() != m_d->colorSpace) {
c.convertTo(m_d->colorSpace);
}
m_d->currentColor = c;
Q_EMIT sigNewColor(c);
m_d->imagesNeedUpdate = true;
update();
}
void KisVisualColorSelectorShape::setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer)
{
if (displayRenderer) {
if (m_d->displayRenderer) {
m_d->displayRenderer->disconnect(this);
}
m_d->displayRenderer = displayRenderer;
} else {
m_d->displayRenderer = KoDumbColorDisplayRenderer::instance();
}
connect(m_d->displayRenderer, SIGNAL(displayConfigurationChanged()),
SLOT(updateFromChangedDisplayRenderer()), Qt::UniqueConnection);
}
void KisVisualColorSelectorShape::updateFromChangedDisplayRenderer()
{
//qDebug() << this << "updateFromChangedDisplayRenderer();";
m_d->imagesNeedUpdate = true;
updateCursor();
//m_d->currentColor = convertShapeCoordinateToKoColor(getCursorPosition());
update();
}
void KisVisualColorSelectorShape::forceImageUpdate()
{
//qDebug() << this << "forceImageUpdate";
m_d->imagesNeedUpdate = true;
}
QColor KisVisualColorSelectorShape::getColorFromConverter(KoColor c){
QColor col;
KoColor color = c;
if (m_d->displayRenderer) {
color.convertTo(m_d->displayRenderer->getPaintingColorSpace());
col = m_d->displayRenderer->toQColor(c);
} else {
col = c.toQColor();
}
return col;
}
void KisVisualColorSelectorShape::slotSetActiveChannels(int channel1, int channel2)
{
//qDebug() << this << "slotSetActiveChannels";
int maxchannel = m_d->colorSpace->colorChannelCount()-1;
m_d->channel1 = qBound(0, channel1, maxchannel);
m_d->channel2 = qBound(0, channel2, maxchannel);
m_d->imagesNeedUpdate = true;
update();
}
bool KisVisualColorSelectorShape::imagesNeedUpdate() const {
return m_d->imagesNeedUpdate;
}
QImage KisVisualColorSelectorShape::getImageMap()
{
//qDebug() << this << ">>>>>>>>> getImageMap()" << m_d->imagesNeedUpdate;
if (m_d->imagesNeedUpdate == true) {
m_d->gradient = QImage(width(), height(), QImage::Format_ARGB32);
m_d->gradient.fill(Qt::transparent);
// KoColor c = m_d->currentColor;
// Fill a buffer with the right kocolors
quint8 *data = new quint8[width() * height() * height()];
quint8 *dataPtr = data;
for (int y = 0; y < m_d->gradient.height(); y++) {
for (int x=0; x < m_d->gradient.width(); x++) {
QPointF newcoordinate = convertWidgetCoordinateToShapeCoordinate(QPoint(x, y));
KoColor c = convertShapeCoordinateToKoColor(newcoordinate);
memcpy(dataPtr, c.data(), m_d->currentColor.colorSpace()->pixelSize());
dataPtr += m_d->currentColor.colorSpace()->pixelSize();
}
}
// Convert the buffer to a qimage
if (m_d->displayRenderer) {
m_d->gradient = m_d->displayRenderer->convertToQImage(m_d->currentColor.colorSpace(), data, width(), height());
}
else {
m_d->gradient = m_d->currentColor.colorSpace()->convertToQImage(data, width(), height(), 0, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
}
delete[] data;
m_d->imagesNeedUpdate = false;
}
return m_d->gradient;
}
KoColor KisVisualColorSelectorShape::convertShapeCoordinateToKoColor(QPointF coordinates, bool cursor)
{
//qDebug() << this << ">>>>>>>>> convertShapeCoordinateToKoColor()" << coordinates;
KoColor c = m_d->currentColor;
QVector channelValues (c.colorSpace()->channelCount());
channelValues.fill(1.0);
c.colorSpace()->normalisedChannelsValue(c.data(), channelValues);
QVector channelValuesDisplay = channelValues;
QVector maxvalue(c.colorSpace()->channelCount());
maxvalue.fill(1.0);
- if (m_d->displayRenderer
- && (m_d->colorSpace->colorDepthId() == Float16BitsColorDepthID
- || m_d->colorSpace->colorDepthId() == Float32BitsColorDepthID
- || m_d->colorSpace->colorDepthId() == Float64BitsColorDepthID)
- && m_d->colorSpace->colorModelId() != LABAColorModelID
- && m_d->colorSpace->colorModelId() != CMYKAColorModelID) {
+ if (m_d->usesOCIO == true) {
for (int ch = 0; ch < maxvalue.size(); ch++) {
KoChannelInfo *channel = m_d->colorSpace->channels()[ch];
maxvalue[ch] = m_d->displayRenderer->maxVisibleFloatValue(channel);
channelValues[ch] = channelValues[ch]/(maxvalue[ch]);
channelValuesDisplay[KoChannelInfo::displayPositionToChannelIndex(ch, m_d->colorSpace->channels())] = channelValues[ch];
}
}
else {
for (int i =0; i < channelValues.size();i++) {
channelValuesDisplay[KoChannelInfo::displayPositionToChannelIndex(i, m_d->colorSpace->channels())] = qBound((float)0.0,channelValues[i], (float)1.0);
}
}
qreal huedivider = 1.0;
qreal huedivider2 = 1.0;
if (m_d->channel1 == 0) {
huedivider = 360.0;
}
if (m_d->channel2 == 0) {
huedivider2 = 360.0;
}
- if (m_d->model != ColorModel::Channel && c.colorSpace()->colorModelId().id() == "RGBA") {
+ if (m_d->model != ColorModel::Channel && m_d->isRGBA == true) {
if (m_d->model == ColorModel::HSV) {
/*
* RGBToHSV has a undefined hue possibility. This means that hue will be -1.
* This can be annoying for dealing with a selector, but I understand it is being
* used for the KoColorSelector... For now implement a qMax here.
*/
QVector inbetween(3);
RGBToHSV(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2]);
inbetween = convertvectorqrealTofloat(getHSX(convertvectorfloatToqreal(inbetween)));
inbetween[m_d->channel1] = coordinates.x()*huedivider;
if (m_d->dimension == Dimensions::twodimensional) {
inbetween[m_d->channel2] = coordinates.y()*huedivider2;
}
if (cursor) {
setHSX(convertvectorfloatToqreal(inbetween));
Q_EMIT sigHSXchange();
}
HSVToRGB(qMax(inbetween[0],(float)0.0), inbetween[1], inbetween[2], &channelValuesDisplay[0], &channelValuesDisplay[1], &channelValuesDisplay[2]);
}
else if (m_d->model == ColorModel::HSL) {
/*
* HSLToRGB can give negative values on the grey. I fixed the fromNormalisedChannel function to clamp,
* but you might want to manually clamp for floating point values.
*/
QVector inbetween(3);
RGBToHSL(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2]);
inbetween = convertvectorqrealTofloat(getHSX(convertvectorfloatToqreal(inbetween)));
inbetween[m_d->channel1] = fmod(coordinates.x()*huedivider, 360.0);
if (m_d->dimension == Dimensions::twodimensional) {
inbetween[m_d->channel2] = coordinates.y()*huedivider2;
}
if (cursor) {
setHSX(convertvectorfloatToqreal(inbetween));
Q_EMIT sigHSXchange();
}
HSLToRGB(qMax(inbetween[0], (float)0.0), inbetween[1], inbetween[2], &channelValuesDisplay[0], &channelValuesDisplay[1], &channelValuesDisplay[2]);
}
else if (m_d->model == ColorModel::HSI) {
/*
* HSI is a modified HSY function.
*/
QVector chan2 = convertvectorfloatToqreal(channelValuesDisplay);
QVector inbetween(3);
RGBToHSI(chan2[0],chan2[1], chan2[2], &inbetween[0], &inbetween[1], &inbetween[2]);
inbetween = getHSX(inbetween);
inbetween[m_d->channel1] = coordinates.x();
if (m_d->dimension == Dimensions::twodimensional) {
inbetween[m_d->channel2] = coordinates.y();
}
if (cursor) {
setHSX(inbetween);
Q_EMIT sigHSXchange();
}
HSIToRGB(inbetween[0], inbetween[1], inbetween[2],&chan2[0],&chan2[1], &chan2[2]);
channelValuesDisplay = convertvectorqrealTofloat(chan2);
}
else /*if (m_d->model == ColorModel::HSY)*/ {
/*
* HSY is pretty slow to render due being a pretty over-the-top function.
* Might be worth investigating whether HCY can be used instead, but I have had
* some weird results with that.
*/
QVector