diff --git a/libs/ui/KisAutoSaveRecoveryDialog.cpp b/libs/ui/KisAutoSaveRecoveryDialog.cpp index 6492b0dbc0..8df6928cd1 100644 --- a/libs/ui/KisAutoSaveRecoveryDialog.cpp +++ b/libs/ui/KisAutoSaveRecoveryDialog.cpp @@ -1,279 +1,279 @@ /* This file is part of the KDE project Copyright (C) 2012 Boudewijn Rempt 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 "KisAutoSaveRecoveryDialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct FileItem { FileItem() : checked(true) {} QImage thumbnail; QString name; QString date; bool checked; }; class FileItemDelegate : public KWidgetItemDelegate { public: FileItemDelegate(QAbstractItemView *itemView, KisAutoSaveRecoveryDialog *dlg) : KWidgetItemDelegate(itemView, dlg) , m_parent(dlg) { } QList createItemWidgets(const QModelIndex& index) const { // a lump of coal and a piece of elastic will get you through the world QWidget *page = new QWidget; QHBoxLayout *layout = new QHBoxLayout(page); QCheckBox *checkBox = new QCheckBox; checkBox->setProperty("fileitem", index.data()); connect(checkBox, SIGNAL(toggled(bool)), m_parent, SLOT(toggleFileItem(bool))); QLabel *thumbnail = new QLabel; QLabel *filename = new QLabel; QLabel *dateModified = new QLabel; layout->addWidget(checkBox); layout->addWidget(thumbnail); layout->addWidget(filename); layout->addWidget(dateModified); page->setFixedSize(600, 200); return QList() << page; } void updateItemWidgets(const QList widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const { FileItem *fileItem = (FileItem*)index.data().value(); QWidget* page= widgets[0]; QHBoxLayout* layout = qobject_cast(page->layout()); QCheckBox *checkBox = qobject_cast(layout->itemAt(0)->widget()); QLabel *thumbnail = qobject_cast(layout->itemAt(1)->widget()); QLabel *filename = qobject_cast(layout->itemAt(2)->widget()); QLabel *modified = qobject_cast(layout->itemAt(3)->widget()); checkBox->setChecked(fileItem->checked); thumbnail->setPixmap(QPixmap::fromImage(fileItem->thumbnail)); filename->setText(fileItem->name); modified->setText(fileItem->date); // move the page _up_ otherwise it will draw relative to the actual postion page->setGeometry(option.rect.translated(0, -option.rect.y())); } void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const { //paint background for selected or hovered item - QStyleOptionViewItemV4 opt = option; + QStyleOptionViewItem opt = option; itemView()->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, 0); } QSize sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const { return QSize(600, 200); } KisAutoSaveRecoveryDialog *m_parent; }; class KisAutoSaveRecoveryDialog::FileItemModel : public QAbstractListModel { public: FileItemModel(QList fileItems, QObject *parent) : QAbstractListModel(parent) , m_fileItems(fileItems){} virtual ~FileItemModel() { qDeleteAll(m_fileItems); m_fileItems.clear(); } int rowCount(const QModelIndex &/*parent*/) const { return m_fileItems.size(); } Qt::ItemFlags flags(const QModelIndex& /*index*/) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; return flags; } QVariant data(const QModelIndex& index, int role) const { if (index.isValid() && index.row() < m_fileItems.size()) { FileItem *item = m_fileItems.at(index.row()); switch (role) { case Qt::DisplayRole: { return QVariant::fromValue((void*)item); } case Qt::SizeHintRole: return QSize(600, 200); } } return QVariant(); } bool setData(const QModelIndex& index, const QVariant& /*value*/, int role) { if (index.isValid() && index.row() < m_fileItems.size()) { if (role == Qt::CheckStateRole) { m_fileItems.at(index.row())->checked = !m_fileItems.at(index.row())->checked; return true; } } return false; } QList m_fileItems; }; KisAutoSaveRecoveryDialog::KisAutoSaveRecoveryDialog(const QStringList &filenames, QWidget *parent) : KoDialog(parent) { setCaption(i18nc("@title:window", "Recover Files")); setButtons( KoDialog::Ok | KoDialog::Cancel | KoDialog::User1 ); setButtonText(KoDialog::User1, i18n("Discard All")); setMinimumSize(650, 500); QWidget *page = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(page); if (filenames.size() == 1) { layout->addWidget(new QLabel(i18n("The following autosave file can be recovered:"))); } else { layout->addWidget(new QLabel(i18n("The following autosave files can be recovered:"))); } m_listView = new QListView(); m_listView->setAcceptDrops(false); KWidgetItemDelegate *delegate = new FileItemDelegate(m_listView, this); m_listView->setItemDelegate(delegate); QList fileItems; Q_FOREACH (const QString &filename, filenames) { FileItem *file = new FileItem(); file->name = filename; #ifdef Q_OS_WIN QString path = QDir::tempPath() + "/" + filename; #else QString path = QDir::homePath() + "/" + filename; #endif // get thumbnail -- almost all Krita-supported formats save a thumbnail KoStore* store = KoStore::createStore(path, KoStore::Read); if (store) { if(store->open(QString("Thumbnails/thumbnail.png")) || store->open(QString("preview.png"))) { // Hooray! No long delay for the user... QByteArray bytes = store->read(store->size()); store->close(); QImage img; img.loadFromData(bytes); file->thumbnail = img.scaled(QSize(200,200), Qt::KeepAspectRatio, Qt::SmoothTransformation); } delete store; } // get the date QDateTime date = QFileInfo(path).lastModified(); file->date = "(" + date.toString(Qt::LocalDate) + ")"; fileItems.append(file); } m_model = new FileItemModel(fileItems, m_listView); m_listView->setModel(m_model); layout->addWidget(m_listView); layout->addWidget(new QLabel(i18n("If you select Cancel, all recoverable files will be kept.\nIf you press OK, selected files will be recovered, the unselected files discarded."))); setMainWidget(page); setAttribute(Qt::WA_DeleteOnClose, false); connect( this, SIGNAL( user1Clicked() ), this, SLOT( slotDeleteAll() ) ); } KisAutoSaveRecoveryDialog::~KisAutoSaveRecoveryDialog() { delete m_listView->itemDelegate(); delete m_model; delete m_listView; } void KisAutoSaveRecoveryDialog::slotDeleteAll() { foreach(FileItem* fileItem, m_model->m_fileItems) { fileItem->checked = false; } accept(); } QStringList KisAutoSaveRecoveryDialog::recoverableFiles() { QStringList files; Q_FOREACH (FileItem* fileItem, m_model->m_fileItems) { if (fileItem->checked) { files << fileItem->name; } } return files; } void KisAutoSaveRecoveryDialog::toggleFileItem(bool toggle) { // I've made better man from a piece of putty and matchstick! QVariant v = sender()->property("fileitem") ; if (v.isValid()) { FileItem *fileItem = (FileItem*)v.value(); fileItem->checked = toggle; } } diff --git a/libs/ui/KisNodeDelegate.cpp b/libs/ui/KisNodeDelegate.cpp index 1fde161ecc..4f71bcd43d 100644 --- a/libs/ui/KisNodeDelegate.cpp +++ b/libs/ui/KisNodeDelegate.cpp @@ -1,872 +1,872 @@ /* 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" typedef KisBaseNode::Property* OptionalProperty; #include class KisNodeDelegate::Private { public: Private() : view(0), edit(0) { } KisNodeView *view; QPointer edit; KisNodeToolTip tip; 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); }; void KisNodeDelegate::slotOnCloseEditor() { KisPart::currentInputManager()->slotFocusOnEnter(true); } KisNodeDelegate::KisNodeDelegate(KisNodeView *view, QObject *parent) : QAbstractItemDelegate(parent) , d(new Private) { d->view = view; QApplication::instance()->installEventFilter(this); connect(this, SIGNAL(closeEditor(QWidget*)), this, SLOT(slotOnCloseEditor())); } 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(); { - QStyleOptionViewItemV4 option = getOptions(o, index); + 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()); 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 { KisPart::currentInputManager()->slotFocusOnEnter(false); 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::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 -QStyleOptionViewItemV4 KisNodeDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index) +QStyleOptionViewItem KisNodeDelegate::getOptions(const QStyleOptionViewItem &o, const QModelIndex &index) { - QStyleOptionViewItemV4 option = o; + 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(); - QStyleOptionProgressBarV2 opt; + 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(); } } diff --git a/libs/ui/KisNodeDelegate.h b/libs/ui/KisNodeDelegate.h index 985c6b8a67..5c7deb5f65 100644 --- a/libs/ui/KisNodeDelegate.h +++ b/libs/ui/KisNodeDelegate.h @@ -1,85 +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); virtual ~KisNodeDelegate(); virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual void setEditorData(QWidget *editor, const QModelIndex &index) const; virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& index) const; protected: virtual bool eventFilter(QObject *object, QEvent *event); private: typedef KisNodeModel Model; typedef KisNodeView View; class Private; Private* const d; - static QStyleOptionViewItemV4 getOptions(const QStyleOptionViewItem &option, const QModelIndex &index); + 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 slotOnCloseEditor(); }; #endif diff --git a/libs/ui/kis_categorized_item_delegate.cpp b/libs/ui/kis_categorized_item_delegate.cpp index 67a37d6d09..c5213aa254 100644 --- a/libs/ui/kis_categorized_item_delegate.cpp +++ b/libs/ui/kis_categorized_item_delegate.cpp @@ -1,132 +1,132 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * Copyright (c) 2014 Mohit Goyal * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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_categorized_item_delegate.h" #include "kis_categorized_list_model.h" #include #include #include -#include +#include #include #include #include #include #include "kis_debug.h" KisCategorizedItemDelegate::KisCategorizedItemDelegate(QObject *parent) : QStyledItemDelegate(parent), m_minimumItemHeight(0) { } void KisCategorizedItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { painter->resetTransform(); if(!index.data(__CategorizedListModelBase::IsHeaderRole).toBool()) { QStyleOptionViewItem sovi(option); if (index.data(__CategorizedListModelBase::isLockableRole).toBool()) { bool locked = index.data(__CategorizedListModelBase::isLockedRole).toBool(); const QIcon icon = locked ? KisIconUtils::loadIcon(koIconName("locked")) : KisIconUtils::loadIcon(koIconName("unlocked")); const int iconSize = qMax(16, m_minimumItemHeight - 2); sovi.decorationPosition = QStyleOptionViewItem::Right; sovi.decorationAlignment = Qt::AlignRight; sovi.decorationSize = QSize(iconSize, iconSize); sovi.features |= QStyleOptionViewItem::HasDecoration; sovi.icon = icon; } QStyledItemDelegate::paint(painter, sovi, index); painter->setOpacity(1); } else { QPalette palette = QApplication::palette(); if(option.state & QStyle::State_MouseOver) painter->fillRect(option.rect, palette.midlight()); else painter->fillRect(option.rect, palette.button()); painter->setBrush(palette.buttonText()); painter->drawText(option.rect, index.data().toString(), QTextOption(Qt::AlignVCenter|Qt::AlignHCenter)); paintTriangle( painter, option.rect.x(), option.rect.y(), option.rect.height(), !index.data(__CategorizedListModelBase::ExpandCategoryRole).toBool() ); } painter->resetTransform(); } QSize KisCategorizedItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { //on first calling this calculates the minimal height of the items if (m_minimumItemHeight == 0) { for(int i=0; irowCount(); i++) { QSize indexSize = QStyledItemDelegate::sizeHint(option, index.model()->index(i, 0)); m_minimumItemHeight = qMax(m_minimumItemHeight, indexSize.height()); /** * Make all the items, including the ones having * checkboxes look the same. */ QStyle *style = QApplication::style(); QStyleOptionButton so; QSize size = style->sizeFromContents(QStyle::CT_CheckBox, &so, QSize(), 0); m_minimumItemHeight = qMax(size.height(), m_minimumItemHeight); } } int width = QStyledItemDelegate::sizeHint(option, index).width(); if (index.data(__CategorizedListModelBase::isLockableRole).toBool()) { width += m_minimumItemHeight; } return QSize(width, m_minimumItemHeight); } void KisCategorizedItemDelegate::paintTriangle(QPainter* painter, qint32 x, qint32 y, qint32 size, bool rotate) const { QPolygonF triangle; triangle.push_back(QPointF(-0.2,-0.2)); triangle.push_back(QPointF( 0.2,-0.2)); triangle.push_back(QPointF( 0.0, 0.2)); QTransform transform; transform.translate(x + size/2, y + size/2); transform.scale(size, size); if(rotate) transform.rotate(-90); QPalette palette = QApplication::palette(); painter->setBrush(palette.buttonText()); painter->drawPolygon(transform.map(triangle)); } diff --git a/libs/widgets/KoDockWidgetTitleBar.cpp b/libs/widgets/KoDockWidgetTitleBar.cpp index 0b2736b8b0..d32c2a475a 100644 --- a/libs/widgets/KoDockWidgetTitleBar.cpp +++ b/libs/widgets/KoDockWidgetTitleBar.cpp @@ -1,375 +1,375 @@ /* This file is part of the KDE project Copyright (c) 2007 Marijn Kruisselbrink Copyright (C) 2007 Thomas Zander 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 "KoDockWidgetTitleBar.h" #include "KoDockWidgetTitleBar_p.h" #include "KoDockWidgetTitleBarButton.h" #include #include #include #include #include #include #include #include #include #include #include static inline bool hasFeature(const QDockWidget *dockwidget, QDockWidget::DockWidgetFeature feature) { return (dockwidget->features() & feature) == feature; } KoDockWidgetTitleBar::KoDockWidgetTitleBar(QDockWidget* dockWidget) : QWidget(dockWidget), d(new Private(this)) { QDockWidget *q = dockWidget; d->floatIcon = kisIcon("docker_float"); d->floatButton = new KoDockWidgetTitleBarButton(this); d->floatButton->setIcon(d->floatIcon); connect(d->floatButton, SIGNAL(clicked()), SLOT(toggleFloating())); d->floatButton->setVisible(true); d->floatButton->setToolTip(i18nc("@info:tooltip", "Float Docker")); d->floatButton->setStyleSheet("border: 0"); d->removeIcon = kisIcon("docker_close"); d->closeButton = new KoDockWidgetTitleBarButton(this); d->closeButton->setIcon(d->removeIcon); connect(d->closeButton, SIGNAL(clicked()), q, SLOT(close())); d->closeButton->setVisible(true); d->closeButton->setToolTip(i18nc("@info:tooltip", "Close Docker")); d->closeButton->setStyleSheet("border: 0"); // border makes the header busy looking (appears on some OSs) d->openIcon = kisIcon("docker_collapse_a"); d->closeIcon = kisIcon("docker_collapse_b"); d->collapseButton = new KoDockWidgetTitleBarButton(this); d->collapseButton->setIcon(d->openIcon); connect(d->collapseButton, SIGNAL(clicked()), SLOT(toggleCollapsed())); d->collapseButton->setVisible(true); d->collapsable = true; d->collapseButton->setToolTip(i18nc("@info:tooltip", "Collapse Docker")); d->collapseButton->setStyleSheet("border: 0"); d->lockIcon = kisIcon("docker_lock_a"); d->lockButton = new KoDockWidgetTitleBarButton(this); d->lockButton->setCheckable(true); d->lockButton->setIcon(d->lockIcon); connect(d->lockButton, SIGNAL(toggled(bool)), SLOT(setLocked(bool))); d->lockButton->setVisible(true); d->lockable = true; d->lockButton->setToolTip(i18nc("@info:tooltip", "Lock Docker")); d->lockButton->setStyleSheet("border: 0"); connect(dockWidget, SIGNAL(featuresChanged(QDockWidget::DockWidgetFeatures)), SLOT(featuresChanged(QDockWidget::DockWidgetFeatures))); connect(dockWidget, SIGNAL(topLevelChanged(bool)), SLOT(topLevelChanged(bool))); d->featuresChanged(0); } KoDockWidgetTitleBar::~KoDockWidgetTitleBar() { delete d; } QSize KoDockWidgetTitleBar::minimumSizeHint() const { return sizeHint(); } QSize KoDockWidgetTitleBar::sizeHint() const { if (isHidden()) { return QSize(0, 0); } QDockWidget *q = qobject_cast(parentWidget()); int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q); // get size of buttons... QSize closeSize(0, 0); if (d->closeButton && hasFeature(q, QDockWidget::DockWidgetClosable)) { closeSize = d->closeButton->sizeHint(); } QSize floatSize(0, 0); if (d->floatButton && hasFeature(q, QDockWidget::DockWidgetFloatable)) { floatSize = d->floatButton->sizeHint(); } QSize hideSize(0, 0); if (d->collapseButton && d->collapsable) { hideSize = d->collapseButton->sizeHint(); } QSize lockSize(0, 0); if (d->lockButton && d->lockable) { lockSize = d->lockButton->sizeHint(); } int buttonHeight = qMax(qMax(qMax(closeSize.height(), floatSize.height()), hideSize.height()), lockSize.height()) + 2; int buttonWidth = closeSize.width() + floatSize.width() + hideSize.width() + lockSize.width(); int height = buttonHeight; if (d->textVisibilityMode == FullTextAlwaysVisible) { // get font size QFontMetrics titleFontMetrics = q->fontMetrics(); int fontHeight = titleFontMetrics.lineSpacing() + 2 * mw; height = qMax(height, fontHeight); } /* * Calculate the width of title and add to the total width of the docker window when collapsed. */ const int titleWidth = (d->textVisibilityMode == FullTextAlwaysVisible) ? (q->fontMetrics().width(q->windowTitle()) + 2*mw) : 0; if (d->preCollapsedWidth > 0) { return QSize(d->preCollapsedWidth, height); } else { if (d->textVisibilityMode == FullTextAlwaysVisible) { return QSize(buttonWidth /*+ height*/ + 2*mw + 2*fw + titleWidth, height); } else { if (q->widget()) { return QSize(qMin(q->widget()->sizeHint().width(), buttonWidth), height); } else { return QSize(buttonWidth, height); } } } } void KoDockWidgetTitleBar::paintEvent(QPaintEvent*) { QStylePainter p(this); QDockWidget *q = qobject_cast(parentWidget()); int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; int mw = q->style()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, 0, q); - QStyleOptionDockWidgetV2 titleOpt; + QStyleOptionDockWidget titleOpt; titleOpt.initFrom(q); QSize collapseButtonSize(0,0); if (d->collapsable) { collapseButtonSize = d->collapseButton->size(); } QSize lockButtonSize(0,0); if (d->lockable) { lockButtonSize = d->lockButton->size(); } titleOpt.rect = QRect(QPoint(fw + mw + collapseButtonSize.width() + lockButtonSize.width(), 0), QSize(geometry().width() - (fw * 2) - mw - collapseButtonSize.width() - lockButtonSize.width(), geometry().height())); titleOpt.title = q->windowTitle(); titleOpt.closable = hasFeature(q, QDockWidget::DockWidgetClosable); titleOpt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable); p.drawControl(QStyle::CE_DockWidgetTitle, titleOpt); } void KoDockWidgetTitleBar::resizeEvent(QResizeEvent*) { QDockWidget *q = qobject_cast(parentWidget()); int fw = q->isFloating() ? q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q) : 0; - QStyleOptionDockWidgetV2 opt; + QStyleOptionDockWidget opt; opt.initFrom(q); opt.rect = QRect(QPoint(fw, fw), QSize(geometry().width() - (fw * 2), geometry().height() - (fw * 2))); opt.title = q->windowTitle(); opt.closable = hasFeature(q, QDockWidget::DockWidgetClosable); opt.floatable = hasFeature(q, QDockWidget::DockWidgetFloatable); QRect floatRect = q->style()->subElementRect(QStyle::SE_DockWidgetFloatButton, &opt, q); if (!floatRect.isNull()) d->floatButton->setGeometry(floatRect); QRect closeRect = q->style()->subElementRect(QStyle::SE_DockWidgetCloseButton, &opt, q); if (!closeRect.isNull()) d->closeButton->setGeometry(closeRect); int top = fw; if (!floatRect.isNull()) top = floatRect.y(); else if (!closeRect.isNull()) top = closeRect.y(); QSize size = d->collapseButton->size(); if (!closeRect.isNull()) { size = d->closeButton->size(); } else if (!floatRect.isNull()) { size = d->floatButton->size(); } QRect collapseRect = QRect(QPoint(fw, top), size); d->collapseButton->setGeometry(collapseRect); size = d->lockButton->size(); if (!closeRect.isNull()) { size = d->closeButton->size(); } else if (!floatRect.isNull()) { size = d->floatButton->size(); } int offset = 0; if (d->collapsable) { offset = collapseRect.width(); } QRect lockRect = QRect(QPoint(fw + 2 + offset, top), size); d->lockButton->setGeometry(lockRect); if (width() < (closeRect.width() + lockRect.width()) + 50) { d->collapsable = false; d->collapseButton->setVisible(false); d->lockButton->setVisible(false); d->lockable = false; } else { d->collapsable = d->collapsableSet; d->collapseButton->setVisible(d->collapsableSet); d->lockButton->setVisible(true); d->lockable = true; } } void KoDockWidgetTitleBar::setCollapsed(bool collapsed) { QDockWidget *q = qobject_cast(parentWidget()); if (q && q->widget() && q->widget()->isHidden() != collapsed) d->toggleCollapsed(); } void KoDockWidgetTitleBar::setLocked(bool locked) { QDockWidget *q = qobject_cast(parentWidget()); d->locked = locked; d->lockButton->blockSignals(true); d->lockButton->setChecked(locked); d->lockButton->blockSignals(false); //qDebug() << "setlocked" << q << d->locked << locked; if (locked) { d->features = q->features(); q->setFeatures(QDockWidget::NoDockWidgetFeatures); } else { q->setFeatures(d->features); } q->toggleViewAction()->setEnabled(!locked); d->closeButton->setEnabled(!locked); d->floatButton->setEnabled(!locked); d->collapseButton->setEnabled(!locked); d->updateIcons(); q->setProperty("Locked", locked); resizeEvent(0); } void KoDockWidgetTitleBar::setCollapsable(bool collapsable) { d->collapsableSet = collapsable; d->collapsable = collapsable; d->collapseButton->setVisible(collapsable); } void KoDockWidgetTitleBar::setTextVisibilityMode(TextVisibilityMode textVisibilityMode) { d->textVisibilityMode = textVisibilityMode; } void KoDockWidgetTitleBar::updateIcons() { d->updateIcons(); } void KoDockWidgetTitleBar::Private::toggleFloating() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); q->setFloating(!q->isFloating()); } void KoDockWidgetTitleBar::Private::topLevelChanged(bool topLevel) { lockButton->setEnabled(!topLevel); } void KoDockWidgetTitleBar::Private::toggleCollapsed() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); if (q == 0) // there does not *have* to be anything on the dockwidget. return; preCollapsedWidth = q->widget()->isHidden() ? -1 : thePublic->width(); q->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); // will be overwritten again next if (q->widget()) { q->widget()->setVisible(q->widget()->isHidden()); collapseButton->setIcon(q->widget()->isHidden() ? kisIcon("docker_collapse_b") : kisIcon("docker_collapse_a")); } } void KoDockWidgetTitleBar::Private::featuresChanged(QDockWidget::DockWidgetFeatures) { QDockWidget *q = qobject_cast(thePublic->parentWidget()); closeButton->setVisible(hasFeature(q, QDockWidget::DockWidgetClosable)); floatButton->setVisible(hasFeature(q, QDockWidget::DockWidgetFloatable)); thePublic->resizeEvent(0); } void KoDockWidgetTitleBar::Private::updateIcons() { QDockWidget *q = qobject_cast(thePublic->parentWidget()); lockIcon = (!locked) ? kisIcon("docker_lock_a") : kisIcon("docker_lock_b"); lockButton->setIcon(lockIcon); // this method gets called when switching themes, so update all of the themed icons now floatButton->setIcon(kisIcon("docker_float")); closeButton->setIcon(kisIcon("docker_close")); if (q->widget()) { collapseButton->setIcon(q->widget()->isHidden() ? kisIcon("docker_collapse_b") : kisIcon("docker_collapse_a")); } thePublic->resizeEvent(0); } //have to include this because of Q_PRIVATE_SLOT #include "moc_KoDockWidgetTitleBar.cpp" diff --git a/plugins/dockers/animation/kis_animation_curves_value_ruler.cpp b/plugins/dockers/animation/kis_animation_curves_value_ruler.cpp index 255673f7ef..47ae88eef1 100644 --- a/plugins/dockers/animation/kis_animation_curves_value_ruler.cpp +++ b/plugins/dockers/animation/kis_animation_curves_value_ruler.cpp @@ -1,138 +1,138 @@ /* * Copyright (c) 2016 Jouni Pentikäinen * * 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_animation_curves_value_ruler.h" #include #include #include #include #include const int MIN_LABEL_SEPARATION = 24; struct KisAnimationCurvesValueRuler::Private { Private() : offset(-300) , scale(1.0) {} float offset; float scale; }; KisAnimationCurvesValueRuler::KisAnimationCurvesValueRuler(QWidget *parent) : QHeaderView(Qt::Vertical, parent) , m_d(new Private()) {} KisAnimationCurvesValueRuler::~KisAnimationCurvesValueRuler() {} float KisAnimationCurvesValueRuler::scaleFactor() const { return -m_d->scale; } float KisAnimationCurvesValueRuler::mapValueToView(float value) const { return -m_d->offset - m_d->scale * value; } float KisAnimationCurvesValueRuler::mapViewToValue(float y) const { return (-m_d->offset - y) / m_d->scale; } void KisAnimationCurvesValueRuler::setOffset(float offset) { m_d->offset = offset; viewport()->update(); } float KisAnimationCurvesValueRuler::offset() const { return m_d->offset; } void KisAnimationCurvesValueRuler::setScale(float scale) { m_d->scale = scale; viewport()->update(); } QSize KisAnimationCurvesValueRuler::sizeHint() const { return QSize(32, 0); } void KisAnimationCurvesValueRuler::paintEvent(QPaintEvent *e) { QPalette palette = qApp->palette(); QPainter painter(viewport()); painter.fillRect(e->rect(), palette.color(QPalette::Button)); QColor textColor = qApp->palette().color(QPalette::ButtonText); const QPen labelPen = QPen(textColor); - QStyleOptionViewItemV4 option = viewOptions(); + QStyleOptionViewItem option = viewOptions(); const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this); const QColor gridColor = static_cast(gridHint); const QPen gridPen = QPen(gridColor); qreal minStep = MIN_LABEL_SEPARATION / m_d->scale; int minExp = ceil(log10(minStep)); qreal majorStep = pow(10, minExp); qreal minorStep = 0.1 * majorStep; if (0.2 * majorStep * m_d->scale > MIN_LABEL_SEPARATION) { majorStep *= 0.2; minExp--; } else if (0.5 * majorStep * m_d->scale > MIN_LABEL_SEPARATION) { majorStep *= 0.5; minExp--; } qreal min = mapViewToValue(e->rect().bottom()); qreal max = mapViewToValue(e->rect().top()); qreal value = majorStep * floor(min/majorStep); while (value < max) { painter.setPen(gridPen); qreal nextMajor = value + majorStep; while (value < nextMajor) { value += minorStep; int y = mapValueToView(value); painter.drawLine(30, y, 32, y); } int y = mapValueToView(value); painter.drawLine(24, y, 32, y); painter.setPen(labelPen); const QString label = QString::number(value, 'f', qMax(0,-minExp)); const QRect textRect = QRect(0, y, 30, MIN_LABEL_SEPARATION); painter.drawText(textRect, label, QTextOption(Qt::AlignRight)); value = nextMajor; } } diff --git a/plugins/dockers/animation/kis_equalizer_button.cpp b/plugins/dockers/animation/kis_equalizer_button.cpp index a79f39457d..82e44e7fd0 100644 --- a/plugins/dockers/animation/kis_equalizer_button.cpp +++ b/plugins/dockers/animation/kis_equalizer_button.cpp @@ -1,160 +1,160 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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_equalizer_button.h" #include #include #include #include #include "timeline_color_scheme.h" #include "kis_global.h" #include "kis_debug.h" struct KisEqualizerButton::Private { Private(KisEqualizerButton *_q) : q(_q), isRightmost(false), isHovering(false) {} QRect boundingRect() const; QRect fillingRect() const; KisEqualizerButton *q; bool isRightmost; bool isHovering; }; KisEqualizerButton::KisEqualizerButton(QWidget *parent) : QAbstractButton(parent), m_d(new Private(this)) { setFocusPolicy(Qt::WheelFocus); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } KisEqualizerButton::~KisEqualizerButton() { } void KisEqualizerButton::setRightmost(bool value) { m_d->isRightmost = value; } QRect KisEqualizerButton::Private::boundingRect() const { QRect bounds = q->rect().adjusted(0, 0, -isRightmost, 0); return bounds; } QRect KisEqualizerButton::Private::fillingRect() const { const int offset = 3; QRect filling = boundingRect().adjusted(offset + 1, offset + 1, -offset, -offset); return filling; } void KisEqualizerButton::paintEvent(QPaintEvent *event) { Q_UNUSED(event); const QRect bounds = m_d->boundingRect(); const QRect filling = m_d->fillingRect(); const QColor backgroundColor = palette().color(QPalette::Base); QPainter p(this); { // draw border - QStyleOptionViewItemV4 option; // empty! + QStyleOptionViewItem option; // empty! const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this); const QColor gridColor = static_cast(gridHint); const QPen gridPen(gridColor); p.setPen(gridPen); p.setBrush(backgroundColor); p.drawRect(bounds); } { QColor fillColor = TimelineColorScheme::instance()->onionSkinsButtonColor(); QColor frameColor = TimelineColorScheme::instance()-> onionSkinsSliderEnabledColor(); if (isChecked() || hasFocus() || m_d->isHovering) { p.setPen(hasFocus() || m_d->isHovering ? frameColor : Qt::transparent); p.setBrush(isChecked() ? fillColor : Qt::transparent); p.drawRect(filling); } } QString textValue = text(); { // draw text QPalette::ColorRole textRole = QPalette::Text; //Draw text shadow, This will increase readability when the background of same color QRect shadowRect(bounds); shadowRect.translate(1,1); QColor textColor = palette().color(textRole); QColor shadowColor = (textColor.value() <= 128) ? QColor(255,255,255,160) : QColor(0,0,0,160); int flags = Qt::AlignCenter | Qt::TextHideMnemonic; p.setPen(shadowColor); p.drawText(shadowRect, flags, textValue); p.setPen(textColor); p.drawText(bounds, flags, textValue); } } QSize KisEqualizerButton::sizeHint() const { QFontMetrics metrics(this->font()); const int minHeight = metrics.height() + 10; return QSize(15, minHeight); } QSize KisEqualizerButton::minimumSizeHint() const { QSize sh = sizeHint(); return QSize(10, sh.height()); } void KisEqualizerButton::enterEvent(QEvent *event) { Q_UNUSED(event); m_d->isHovering = true; update(); } void KisEqualizerButton::leaveEvent(QEvent *event) { Q_UNUSED(event); m_d->isHovering = false; update(); } diff --git a/plugins/dockers/animation/kis_equalizer_slider.cpp b/plugins/dockers/animation/kis_equalizer_slider.cpp index f9e20cf643..ee99dc2441 100644 --- a/plugins/dockers/animation/kis_equalizer_slider.cpp +++ b/plugins/dockers/animation/kis_equalizer_slider.cpp @@ -1,224 +1,224 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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_equalizer_slider.h" #include #include #include #include #include #include "kis_global.h" #include "kis_debug.h" #include "timeline_color_scheme.h" struct KisEqualizerSlider::Private { Private(KisEqualizerSlider *_q) : q(_q), isRightmost(false), toggleState(true) {} KisEqualizerSlider *q; bool isRightmost; bool toggleState; QRect boundingRect() const; QRect sliderRect() const; int mousePosToValue(const QPoint &pt, bool round) const; }; KisEqualizerSlider::KisEqualizerSlider(QWidget *parent) : QAbstractSlider(parent), m_d(new Private(this)) { setOrientation(Qt::Vertical); setFocusPolicy(Qt::WheelFocus); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } KisEqualizerSlider::~KisEqualizerSlider() { } void KisEqualizerSlider::setRightmost(bool value) { m_d->isRightmost = value; } void KisEqualizerSlider::setToggleState(bool value) { m_d->toggleState = value; update(); } QRect KisEqualizerSlider::Private::boundingRect() const { QRect bounds = q->rect().adjusted(0, 0, -isRightmost, -1); return bounds; } QRect KisEqualizerSlider::Private::sliderRect() const { const int offset = 3; QRect filling = boundingRect().adjusted(offset + 1, offset + 1, -offset, -offset); return filling; } int KisEqualizerSlider::Private::mousePosToValue(const QPoint &pt, bool round) const { const QRect areaRect = sliderRect(); int rawValue = -pt.y() + (areaRect.top() + areaRect.height()); int maxRawValue = areaRect.height(); int value = QStyle::sliderValueFromPosition(q->minimum(), q->maximum(), rawValue, maxRawValue); if (round) { const int singleStep = q->singleStep(); value = ((value + singleStep / 2) / singleStep) * singleStep; } return value; } void KisEqualizerSlider::mousePressEvent(QMouseEvent *ev) { if (maximum() == minimum() || (ev->buttons() ^ ev->button())) { ev->ignore(); return; } const bool precise = ev->modifiers() & Qt::ControlModifier || ev->button() == Qt::RightButton; int value = m_d->mousePosToValue(ev->pos(), !precise); setSliderPosition(value); triggerAction(SliderMove); setRepeatAction(SliderNoAction); } void KisEqualizerSlider::mouseMoveEvent(QMouseEvent *ev) { if (ev->modifiers() & Qt::ShiftModifier && !rect().contains(ev->pos())) { ev->ignore(); return; } const bool precise = ev->modifiers() & Qt::ControlModifier || ev->buttons() & Qt::RightButton; int value = m_d->mousePosToValue(ev->pos(), !precise); setSliderPosition(value); triggerAction(SliderMove); setRepeatAction(SliderNoAction); } void KisEqualizerSlider::mouseReleaseEvent(QMouseEvent *ev) { Q_UNUSED(ev); } QSize KisEqualizerSlider::sizeHint() const { return QSize(25, 150); } QSize KisEqualizerSlider::minimumSizeHint() const { return QSize(10, 40); } void KisEqualizerSlider::paintEvent(QPaintEvent *event) { Q_UNUSED(event); const QRect bounds = m_d->boundingRect(); const QColor backgroundColor = palette().color(QPalette::Base); QPainter p(this); { // draw border - QStyleOptionViewItemV4 option; // empty! + QStyleOptionViewItem option; // empty! const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this); const QColor gridColor = static_cast(gridHint); const QPen gridPen(gridColor); p.setPen(gridPen); p.setBrush(backgroundColor); p.drawRect(bounds); } { // draw slider QRect sliderRect = m_d->sliderRect(); const int sliderPos = QStyle::sliderPositionFromValue(minimum(), maximum(), value(), sliderRect.height()); sliderRect.adjust(0, sliderRect.height() - sliderPos, 0, 0); p.setPen(Qt::transparent); QColor color = m_d->toggleState ? TimelineColorScheme::instance()->onionSkinsSliderEnabledColor() : TimelineColorScheme::instance()->onionSkinsSliderDisabledColor(); p.setBrush(color); p.drawRect(sliderRect); } QString textValue = QString::number(value()); /* Text isn't really needed for onion skinning and makes it look a bit cluttered. Uncomment this out of that changes. { // draw text QPalette::ColorRole textRole = QPalette::Text; //Draw text shadow, This will increase readability when the background of same color QRect shadowRect(bounds); shadowRect.translate(1,1); QColor textColor = palette().color(textRole); QColor shadowColor = (textColor.value() <= 128) ? QColor(255,255,255,160) : QColor(0,0,0,160); p.setPen(shadowColor); p.drawText(shadowRect, Qt::AlignCenter, textValue); p.setPen(textColor); p.drawText(bounds, Qt::AlignCenter, textValue); } */ // draw focus rect if (hasFocus()) { QStyleOptionFocusRect fropt; fropt.initFrom(this); fropt.backgroundColor = backgroundColor; int dfw1 = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &fropt, this) + 1, dfw2 = dfw1 * 2; fropt.rect = kisGrowRect(bounds, -dfw1 - dfw2); style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, &p, this); } } diff --git a/plugins/dockers/animation/timeline_frames_view.cpp b/plugins/dockers/animation/timeline_frames_view.cpp index 2ce8a961cf..e0c836ad6e 100644 --- a/plugins/dockers/animation/timeline_frames_view.cpp +++ b/plugins/dockers/animation/timeline_frames_view.cpp @@ -1,905 +1,905 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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 "timeline_frames_view.h" #include "timeline_frames_model.h" #include "timeline_ruler_header.h" #include "timeline_layers_header.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "timeline_frames_item_delegate.h" #include "kis_zoom_button.h" #include "kis_icon_utils.h" #include "kis_animation_utils.h" #include "kis_custom_modifiers_catcher.h" #include "kis_action.h" #include "kis_signal_compressor.h" #include "kis_time_range.h" #include "kis_color_label_selector_widget.h" typedef QPair QItemViewPaintPair; typedef QList QItemViewPaintPairs; struct TimelineFramesView::Private { Private(TimelineFramesView *_q) : q(_q), fps(1), zoomStillPointIndex(-1), zoomStillPointOriginalOffset(0), dragInProgress(false), dragWasSuccessful(false), modifiersCatcher(0), selectionChangedCompressor(300, KisSignalCompressor::FIRST_INACTIVE) {} TimelineFramesView *q; TimelineFramesModel *model; TimelineRulerHeader *horizontalRuler; TimelineLayersHeader *layersHeader; int fps; int zoomStillPointIndex; int zoomStillPointOriginalOffset; QPoint initialDragPanValue; QPoint initialDragPanPos; QToolButton *addLayersButton; KisAction *showHideLayerAction; KisColorLabelSelectorWidget *colorSelector; QWidgetAction *colorSelectorAction; KisColorLabelSelectorWidget *multiframeColorSelector; QWidgetAction *multiframeColorSelectorAction; QMenu *layerEditingMenu; QMenu *existingLayersMenu; QMenu *frameCreationMenu; QMenu *frameEditingMenu; QMenu *multipleFrameEditingMenu; QMap globalActions; KisZoomButton *zoomDragButton; bool dragInProgress; bool dragWasSuccessful; KisCustomModifiersCatcher *modifiersCatcher; QPoint lastPressedPosition; KisSignalCompressor selectionChangedCompressor; - QStyleOptionViewItemV4 viewOptionsV4() const; + QStyleOptionViewItem viewOptionsV4() const; QItemViewPaintPairs draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const; QPixmap renderToPixmap(const QModelIndexList &indexes, QRect *r) const; }; TimelineFramesView::TimelineFramesView(QWidget *parent) : QTableView(parent), m_d(new Private(this)) { m_d->modifiersCatcher = new KisCustomModifiersCatcher(this); m_d->modifiersCatcher->addModifier("pan-zoom", Qt::Key_Space); m_d->modifiersCatcher->addModifier("offset-frame", Qt::Key_Alt); setCornerButtonEnabled(false); setSelectionBehavior(QAbstractItemView::SelectItems); setSelectionMode(QAbstractItemView::ExtendedSelection); setItemDelegate(new TimelineFramesItemDelegate(this)); setDragEnabled(true); setDragDropMode(QAbstractItemView::DragDrop); setAcceptDrops(true); setDropIndicatorShown(true); setDefaultDropAction(Qt::MoveAction); m_d->horizontalRuler = new TimelineRulerHeader(this); this->setHorizontalHeader(m_d->horizontalRuler); m_d->layersHeader = new TimelineLayersHeader(this); m_d->layersHeader->setSectionResizeMode(QHeaderView::Fixed); m_d->layersHeader->setDefaultSectionSize(24); m_d->layersHeader->setMinimumWidth(60); m_d->layersHeader->setHighlightSections(true); this->setVerticalHeader(m_d->layersHeader); connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), SLOT(slotUpdateInfiniteFramesCount())); connect(horizontalScrollBar(), SIGNAL(sliderReleased()), SLOT(slotUpdateInfiniteFramesCount())); m_d->addLayersButton = new QToolButton(this); m_d->addLayersButton->setAutoRaise(true); m_d->addLayersButton->setIcon(KisIconUtils::loadIcon("addlayer")); m_d->addLayersButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_d->addLayersButton->setPopupMode(QToolButton::InstantPopup); m_d->layerEditingMenu = new QMenu(this); m_d->layerEditingMenu->addAction(KisAnimationUtils::newLayerActionName, this, SLOT(slotAddNewLayer())); m_d->existingLayersMenu = m_d->layerEditingMenu->addMenu(KisAnimationUtils::addExistingLayerActionName); m_d->layerEditingMenu->addSeparator(); m_d->showHideLayerAction = new KisAction(KisAnimationUtils::showLayerActionName, this); m_d->showHideLayerAction->setActivationFlags(KisAction::ACTIVE_LAYER); connect(m_d->showHideLayerAction, SIGNAL(triggered()), SLOT(slotHideLayerFromTimeline())); m_d->showHideLayerAction->setCheckable(true); m_d->globalActions.insert("show_in_timeline", m_d->showHideLayerAction); m_d->layerEditingMenu->addAction(m_d->showHideLayerAction); m_d->layerEditingMenu->addAction(KisAnimationUtils::removeLayerActionName, this, SLOT(slotRemoveLayer())); connect(m_d->existingLayersMenu, SIGNAL(aboutToShow()), SLOT(slotUpdateLayersMenu())); connect(m_d->existingLayersMenu, SIGNAL(triggered(QAction*)), SLOT(slotAddExistingLayer(QAction*))); connect(m_d->layersHeader, SIGNAL(sigRequestContextMenu(const QPoint&)), SLOT(slotLayerContextMenuRequested(const QPoint&))); m_d->addLayersButton->setMenu(m_d->layerEditingMenu); m_d->frameCreationMenu = new QMenu(this); m_d->frameCreationMenu->addAction(KisAnimationUtils::addFrameActionName, this, SLOT(slotNewFrame())); m_d->frameCreationMenu->addAction(KisAnimationUtils::duplicateFrameActionName, this, SLOT(slotCopyFrame())); m_d->colorSelector = new KisColorLabelSelectorWidget(this); m_d->colorSelectorAction = new QWidgetAction(this); m_d->colorSelectorAction->setDefaultWidget(m_d->colorSelector); connect(m_d->colorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged); m_d->frameEditingMenu = new QMenu(this); m_d->frameEditingMenu->addAction(KisAnimationUtils::removeFrameActionName, this, SLOT(slotRemoveFrame())); m_d->frameEditingMenu->addAction(m_d->colorSelectorAction); m_d->multiframeColorSelector = new KisColorLabelSelectorWidget(this); m_d->multiframeColorSelectorAction = new QWidgetAction(this); m_d->multiframeColorSelectorAction->setDefaultWidget(m_d->multiframeColorSelector); connect(m_d->multiframeColorSelector, &KisColorLabelSelectorWidget::currentIndexChanged, this, &TimelineFramesView::slotColorLabelChanged); m_d->multipleFrameEditingMenu = new QMenu(this); m_d->multipleFrameEditingMenu->addAction(KisAnimationUtils::removeFramesActionName, this, SLOT(slotRemoveFrame())); m_d->multipleFrameEditingMenu->addAction(m_d->multiframeColorSelectorAction); m_d->zoomDragButton = new KisZoomButton(this); m_d->zoomDragButton->setAutoRaise(true); m_d->zoomDragButton->setIcon(KisIconUtils::loadIcon("zoom-horizontal")); m_d->zoomDragButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); m_d->zoomDragButton->setToolTip(i18nc("@info:tooltip", "Zoom Timeline. Hold down and drag left or right.")); m_d->zoomDragButton->setPopupMode(QToolButton::InstantPopup); connect(m_d->zoomDragButton, SIGNAL(zoomLevelChanged(qreal)), SLOT(slotZoomButtonChanged(qreal))); connect(m_d->zoomDragButton, SIGNAL(zoomStarted(qreal)), SLOT(slotZoomButtonPressed(qreal))); setFramesPerSecond(12); setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); connect(&m_d->selectionChangedCompressor, SIGNAL(timeout()), SLOT(slotSelectionChanged())); } TimelineFramesView::~TimelineFramesView() { } QMap TimelineFramesView::globalActions() const { return m_d->globalActions; } void resizeToMinimalSize(QAbstractButton *w, int minimalSize) { QSize buttonSize = w->sizeHint(); if (buttonSize.height() > minimalSize) { buttonSize = QSize(minimalSize, minimalSize); } w->resize(buttonSize); } void TimelineFramesView::updateGeometries() { QTableView::updateGeometries(); const int availableHeight = m_d->horizontalRuler->height(); const int margin = 2; const int minimalSize = availableHeight - 2 * margin; resizeToMinimalSize(m_d->addLayersButton, minimalSize); resizeToMinimalSize(m_d->zoomDragButton, minimalSize); int x = 2 * margin; int y = (availableHeight - minimalSize) / 2; m_d->addLayersButton->move(x, 2 * y); const int availableWidth = m_d->layersHeader->width(); x = availableWidth - margin - minimalSize; m_d->zoomDragButton->move(x, 2 * y); } void TimelineFramesView::setModel(QAbstractItemModel *model) { TimelineFramesModel *framesModel = qobject_cast(model); m_d->model = framesModel; QTableView::setModel(model); connect(m_d->model, SIGNAL(headerDataChanged(Qt::Orientation, int, int)), this, SLOT(slotHeaderDataChanged(Qt::Orientation, int, int))); connect(m_d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex))); connect(m_d->model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), this, SLOT(slotReselectCurrentIndex())); connect(m_d->model, SIGNAL(sigInfiniteTimelineUpdateNeeded()), this, SLOT(slotUpdateInfiniteFramesCount())); connect(selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), &m_d->selectionChangedCompressor, SLOT(start())); } void TimelineFramesView::setFramesPerSecond(int fps) { m_d->fps = fps; m_d->horizontalRuler->setFramePerSecond(fps); // For some reason simple update sometimes doesn't work here, so // reset the whole header // // m_d->horizontalRuler->reset(); } void TimelineFramesView::slotZoomButtonPressed(qreal staticPoint) { m_d->zoomStillPointIndex = qIsNaN(staticPoint) ? currentIndex().column() : staticPoint; const int w = m_d->horizontalRuler->defaultSectionSize(); m_d->zoomStillPointOriginalOffset = w * m_d->zoomStillPointIndex - horizontalScrollBar()->value(); } void TimelineFramesView::slotZoomButtonChanged(qreal zoomLevel) { if (m_d->horizontalRuler->setZoom(zoomLevel)) { slotUpdateInfiniteFramesCount(); const int w = m_d->horizontalRuler->defaultSectionSize(); horizontalScrollBar()->setValue(w * m_d->zoomStillPointIndex - m_d->zoomStillPointOriginalOffset); viewport()->update(); } } void TimelineFramesView::slotColorLabelChanged(int label) { Q_FOREACH(QModelIndex index, selectedIndexes()) { m_d->model->setData(index, label, TimelineFramesModel::ColorLabel); } KisImageConfig config; config.setDefaultFrameColorLabel(label); } void TimelineFramesView::slotUpdateInfiniteFramesCount() { if (horizontalScrollBar()->isSliderDown()) return; const int sectionWidth = m_d->horizontalRuler->defaultSectionSize(); const int calculatedIndex = (horizontalScrollBar()->value() + m_d->horizontalRuler->width() - 1) / sectionWidth; m_d->model->setLastVisibleFrame(calculatedIndex); } void TimelineFramesView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { QTableView::currentChanged(current, previous); if (previous.column() != current.column()) { m_d->model->setData(previous, false, TimelineFramesModel::ActiveFrameRole); m_d->model->setData(current, true, TimelineFramesModel::ActiveFrameRole); } } QItemSelectionModel::SelectionFlags TimelineFramesView::selectionCommand(const QModelIndex &index, const QEvent *event) const { // WARNING: Copy-pasted from KisNodeView! Please keep in sync! /** * Qt has a bug: when we Ctrl+click on an item, the item's * selections gets toggled on mouse *press*, whereas usually it is * done on mouse *release*. Therefore the user cannot do a * Ctrl+D&D with the default configuration. This code fixes the * problem by manually returning QItemSelectionModel::NoUpdate * flag when the user clicks on an item and returning * QItemSelectionModel::Toggle on release. */ if (event && (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease) && index.isValid()) { const QMouseEvent *mevent = static_cast(event); if (mevent->button() == Qt::RightButton && selectionModel()->selectedIndexes().contains(index)) { // Allow calling context menu for multiple layers return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonPress && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::NoUpdate; } if (event->type() == QEvent::MouseButtonRelease && (mevent->modifiers() & Qt::ControlModifier)) { return QItemSelectionModel::Toggle; } } return QAbstractItemView::selectionCommand(index, event); } void TimelineFramesView::slotSelectionChanged() { int minColumn = std::numeric_limits::max(); int maxColumn = std::numeric_limits::min(); foreach (const QModelIndex &idx, selectedIndexes()) { if (idx.column() > maxColumn) { maxColumn = idx.column(); } if (idx.column() < minColumn) { minColumn = idx.column(); } } KisTimeRange range; if (maxColumn > minColumn) { range = KisTimeRange(minColumn, maxColumn - minColumn + 1); } m_d->model->setPlaybackRange(range); } void TimelineFramesView::slotReselectCurrentIndex() { QModelIndex index = currentIndex(); currentChanged(index, index); } void TimelineFramesView::slotDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (m_d->model->isPlaybackActive()) return; int selectedColumn = -1; for (int j = topLeft.column(); j <= bottomRight.column(); j++) { QVariant value = m_d->model->data( m_d->model->index(topLeft.row(), j), TimelineFramesModel::ActiveFrameRole); if (value.isValid() && value.toBool()) { selectedColumn = j; break; } } QModelIndex index = currentIndex(); if (!index.isValid() && selectedColumn < 0) { return; } if (selectedColumn == -1) { selectedColumn = index.column(); } if (selectedColumn != index.column() && !m_d->dragInProgress) { int row= index.isValid() ? index.row() : 0; setCurrentIndex(m_d->model->index(row, selectedColumn)); } } void TimelineFramesView::slotHeaderDataChanged(Qt::Orientation orientation, int first, int last) { Q_UNUSED(first); Q_UNUSED(last); if (orientation == Qt::Horizontal) { const int newFps = m_d->model->headerData(0, Qt::Horizontal, TimelineFramesModel::FramesPerSecondRole).toInt(); if (newFps != m_d->fps) { setFramesPerSecond(newFps); } } else /* if (orientation == Qt::Vertical) */ { updateShowInTimeline(); } } void TimelineFramesView::rowsInserted(const QModelIndex& parent, int start, int end) { QTableView::rowsInserted(parent, start, end); updateShowInTimeline(); } inline bool isIndexDragEnabled(QAbstractItemModel *model, const QModelIndex &index) { return (model->flags(index) & Qt::ItemIsDragEnabled); } -QStyleOptionViewItemV4 TimelineFramesView::Private::viewOptionsV4() const +QStyleOptionViewItem TimelineFramesView::Private::viewOptionsV4() const { - QStyleOptionViewItemV4 option = q->viewOptions(); + QStyleOptionViewItem option = q->viewOptions(); option.locale = q->locale(); option.locale.setNumberOptions(QLocale::OmitGroupSeparator); option.widget = q; return option; } QItemViewPaintPairs TimelineFramesView::Private::draggablePaintPairs(const QModelIndexList &indexes, QRect *r) const { Q_ASSERT(r); QRect &rect = *r; const QRect viewportRect = q->viewport()->rect(); QItemViewPaintPairs ret; for (int i = 0; i < indexes.count(); ++i) { const QModelIndex &index = indexes.at(i); const QRect current = q->visualRect(index); if (current.intersects(viewportRect)) { ret += qMakePair(current, index); rect |= current; } } rect &= viewportRect; return ret; } QPixmap TimelineFramesView::Private::renderToPixmap(const QModelIndexList &indexes, QRect *r) const { Q_ASSERT(r); QItemViewPaintPairs paintPairs = draggablePaintPairs(indexes, r); if (paintPairs.isEmpty()) return QPixmap(); QPixmap pixmap(r->size()); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); - QStyleOptionViewItemV4 option = viewOptionsV4(); + QStyleOptionViewItem option = viewOptionsV4(); option.state |= QStyle::State_Selected; for (int j = 0; j < paintPairs.count(); ++j) { option.rect = paintPairs.at(j).first.translated(-r->topLeft()); const QModelIndex ¤t = paintPairs.at(j).second; //adjustViewOptionsForIndex(&option, current); q->itemDelegate(current)->paint(&painter, option, current); } return pixmap; } void TimelineFramesView::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes = selectionModel()->selectedIndexes(); if (!indexes.isEmpty() && m_d->modifiersCatcher->modifierPressed("offset-frame")) { QVector rows; int leftmostColumn = std::numeric_limits::max(); Q_FOREACH (const QModelIndex &index, indexes) { leftmostColumn = qMin(leftmostColumn, index.column()); if (!rows.contains(index.row())) { rows.append(index.row()); } } const int lastColumn = m_d->model->columnCount() - 1; selectionModel()->clear(); Q_FOREACH (const int row, rows) { QItemSelection sel(m_d->model->index(row, leftmostColumn), m_d->model->index(row, lastColumn)); selectionModel()->select(sel, QItemSelectionModel::Select); } supportedActions = Qt::MoveAction; { QModelIndexList indexes = selectedIndexes(); for(int i = indexes.count() - 1 ; i >= 0; --i) { if (!isIndexDragEnabled(m_d->model, indexes.at(i))) indexes.removeAt(i); } selectionModel()->clear(); if (indexes.count() > 0) { QMimeData *data = m_d->model->mimeData(indexes); if (!data) return; QRect rect; QPixmap pixmap = m_d->renderToPixmap(indexes, &rect); rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); drag->setHotSpot(m_d->lastPressedPosition - rect.topLeft()); drag->exec(supportedActions, Qt::MoveAction); setCurrentIndex(currentIndex()); } } } else { /** * Workaround for Qt5's bugs: * * 1) Qt doesn't treat selection the selection on D&D * correctly, so we save it in advance and restore * afterwards. * * 2) There is a private variable in QAbstractItemView: * QAbstractItemView::Private::currentSelectionStartIndex. * It is initialized *only* when the setCurrentIndex() is called * explicitly on the view object, not on the selection model. * Therefore we should explicitly call setCurrentIndex() after * D&D, even if it already has *correct* value! * * 2) We should also call selectionModel()->select() * explicitly. There are two reasons for it: 1) Qt doesn't * maintain selection over D&D; 2) when reselecting single * element after D&D, Qt goes crazy, because it tries to * read *global* keyboard modifiers. Therefore if we are * dragging with Shift or Ctrl pressed it'll get crazy. So * just reset it explicitly. */ QModelIndexList selectionBefore = selectionModel()->selectedIndexes(); QModelIndex currentBefore = selectionModel()->currentIndex(); // initialize a global status variable m_d->dragWasSuccessful = false; QAbstractItemView::startDrag(supportedActions); QModelIndex newCurrent; QPoint selectionOffset; if (m_d->dragWasSuccessful) { newCurrent = currentIndex(); selectionOffset = QPoint(newCurrent.column() - currentBefore.column(), newCurrent.row() - currentBefore.row()); } else { newCurrent = currentBefore; selectionOffset = QPoint(); } setCurrentIndex(newCurrent); selectionModel()->clearSelection(); Q_FOREACH (const QModelIndex &idx, selectionBefore) { QModelIndex newIndex = model()->index(idx.row() + selectionOffset.y(), idx.column() + selectionOffset.x()); selectionModel()->select(newIndex, QItemSelectionModel::Select); } } } void TimelineFramesView::dragEnterEvent(QDragEnterEvent *event) { m_d->dragInProgress = true; m_d->model->setScrubState(true); QTableView::dragEnterEvent(event); } void TimelineFramesView::dragMoveEvent(QDragMoveEvent *event) { m_d->dragInProgress = true; m_d->model->setScrubState(true); QTableView::dragMoveEvent(event); if (event->isAccepted()) { QModelIndex index = indexAt(event->pos()); if (!m_d->model->canDropFrameData(event->mimeData(), index)) { event->ignore(); } else { selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); } } } void TimelineFramesView::dropEvent(QDropEvent *event) { m_d->dragInProgress = false; m_d->model->setScrubState(false); QAbstractItemView::dropEvent(event); m_d->dragWasSuccessful = event->isAccepted(); } void TimelineFramesView::dragLeaveEvent(QDragLeaveEvent *event) { m_d->dragInProgress = false; m_d->model->setScrubState(false); QAbstractItemView::dragLeaveEvent(event); } void TimelineFramesView::mousePressEvent(QMouseEvent *event) { QPersistentModelIndex index = indexAt(event->pos()); if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { if (event->button() == Qt::RightButton) { // TODO: try calculate index under mouse cursor even when // it is outside any visible row qreal staticPoint = index.isValid() ? index.column() : currentIndex().column(); m_d->zoomDragButton->beginZoom(event->pos(), staticPoint); } else if (event->button() == Qt::LeftButton) { m_d->initialDragPanPos = event->pos(); m_d->initialDragPanValue = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); } event->accept(); } else if (event->button() == Qt::RightButton) { int numSelectedItems = selectionModel()->selectedIndexes().size(); if (index.isValid() && numSelectedItems <= 1 && m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { model()->setData(index, true, TimelineFramesModel::ActiveLayerRole); model()->setData(index, true, TimelineFramesModel::ActiveFrameRole); setCurrentIndex(index); if (model()->data(index, TimelineFramesModel::FrameExistsRole).toBool() || model()->data(index, TimelineFramesModel::SpecialKeyframeExists).toBool()) { { KisSignalsBlocker b(m_d->colorSelector); QVariant colorLabel = index.data(TimelineFramesModel::ColorLabel); int labelIndex = colorLabel.isValid() ? colorLabel.toInt() : 0; m_d->colorSelector->setCurrentIndex(labelIndex); } m_d->frameEditingMenu->exec(event->globalPos()); } else { m_d->frameCreationMenu->exec(event->globalPos()); } } else if (numSelectedItems > 1) { int labelIndex = 0; bool haveFrames = false; Q_FOREACH(QModelIndex index, selectedIndexes()) { haveFrames |= index.data(TimelineFramesModel::FrameExistsRole).toBool(); QVariant colorLabel = index.data(TimelineFramesModel::ColorLabel); if (colorLabel.isValid()) { if (labelIndex == 0) { labelIndex = colorLabel.toInt(); } else { labelIndex = 0; break; } } } if (!haveFrames) { m_d->multiframeColorSelectorAction->setVisible(false); } else { KisSignalsBlocker b(m_d->multiframeColorSelector); m_d->multiframeColorSelector->setCurrentIndex(labelIndex); m_d->multiframeColorSelectorAction->setVisible(true); } m_d->multipleFrameEditingMenu->exec(event->globalPos()); } } else { if (index.isValid()) { m_d->model->setLastClickedIndex(index); } m_d->lastPressedPosition = QPoint(horizontalOffset(), verticalOffset()) + event->pos(); QAbstractItemView::mousePressEvent(event); } } void TimelineFramesView::mouseMoveEvent(QMouseEvent *e) { if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { if (e->buttons() & Qt::RightButton) { m_d->zoomDragButton->continueZoom(e->pos()); } else if (e->buttons() & Qt::LeftButton) { QPoint diff = e->pos() - m_d->initialDragPanPos; QPoint offset = QPoint(m_d->initialDragPanValue.x() - diff.x(), m_d->initialDragPanValue.y() - diff.y()); const int height = m_d->layersHeader->defaultSectionSize(); horizontalScrollBar()->setValue(offset.x()); verticalScrollBar()->setValue(offset.y() / height); } e->accept(); } else { m_d->model->setScrubState(true); QTableView::mouseMoveEvent(e); } } void TimelineFramesView::mouseReleaseEvent(QMouseEvent *e) { if (m_d->modifiersCatcher->modifierPressed("pan-zoom")) { e->accept(); } else { m_d->model->setScrubState(false); QTableView::mouseReleaseEvent(e); } } void TimelineFramesView::wheelEvent(QWheelEvent *e) { QModelIndex index = currentIndex(); int column= -1; if (index.isValid()) { column= index.column() + ((e->delta() > 0) ? 1 : -1); } if (column >= 0 && !m_d->dragInProgress) { setCurrentIndex(m_d->model->index(index.row(), column)); } } void TimelineFramesView::slotUpdateLayersMenu() { QAction *action = 0; m_d->existingLayersMenu->clear(); QVariant value = model()->headerData(0, Qt::Vertical, TimelineFramesModel::OtherLayersRole); if (value.isValid()) { TimelineFramesModel::OtherLayersList list = value.value(); int i = 0; Q_FOREACH (const TimelineFramesModel::OtherLayer &l, list) { action = m_d->existingLayersMenu->addAction(l.name); action->setData(i++); } } } void TimelineFramesView::slotLayerContextMenuRequested(const QPoint &globalPos) { m_d->layerEditingMenu->exec(globalPos); } void TimelineFramesView::updateShowInTimeline() { const int row = m_d->model->activeLayerRow(); const bool status = m_d->model->headerData(row, Qt::Vertical, TimelineFramesModel::LayerUsedInTimelineRole).toBool(); m_d->showHideLayerAction->setChecked(status); } void TimelineFramesView::slotAddNewLayer() { QModelIndex index = currentIndex(); const int newRow = index.isValid() ? index.row() : 0; model()->insertRow(newRow); } void TimelineFramesView::slotAddExistingLayer(QAction *action) { QVariant value = action->data(); if (value.isValid()) { QModelIndex index = currentIndex(); const int newRow = index.isValid() ? index.row() + 1 : 0; m_d->model->insertOtherLayer(value.toInt(), newRow); } } void TimelineFramesView::slotRemoveLayer() { QModelIndex index = currentIndex(); if (!index.isValid()) return; model()->removeRow(index.row()); } void TimelineFramesView::slotHideLayerFromTimeline() { const int row = m_d->model->activeLayerRow(); const bool status = m_d->model->headerData(row, Qt::Vertical, TimelineFramesModel::LayerUsedInTimelineRole).toBool(); m_d->model->setHeaderData(row, Qt::Vertical, !status, TimelineFramesModel::LayerUsedInTimelineRole); } void TimelineFramesView::slotNewFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->createFrame(index); } void TimelineFramesView::slotCopyFrame() { QModelIndex index = currentIndex(); if (!index.isValid() || !m_d->model->data(index, TimelineFramesModel::FrameEditableRole).toBool()) { return; } m_d->model->copyFrame(index); } void TimelineFramesView::slotRemoveFrame() { QModelIndexList indexes = selectionModel()->selectedIndexes(); for (auto it = indexes.begin(); it != indexes.end(); /*noop*/) { if (!m_d->model->data(*it, TimelineFramesModel::FrameEditableRole).toBool()) { it = indexes.erase(it); } else { ++it; } } if (!indexes.isEmpty()) { m_d->model->removeFrames(indexes); } } diff --git a/plugins/dockers/animation/timeline_ruler_header.cpp b/plugins/dockers/animation/timeline_ruler_header.cpp index 6e57d26435..eb4a626e5f 100644 --- a/plugins/dockers/animation/timeline_ruler_header.cpp +++ b/plugins/dockers/animation/timeline_ruler_header.cpp @@ -1,500 +1,500 @@ /* * Copyright (c) 2015 Dmitry Kazakov * * 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 "timeline_ruler_header.h" #include #include #include #include #include #include #include "kis_time_based_item_model.h" #include "timeline_color_scheme.h" #include "kis_debug.h" struct TimelineRulerHeader::Private { Private() : fps(12), lastPressSectionIndex(-1) {} int fps; QMenu *columnEditingMenu; QAction *insertLeftAction; QAction *insertRightAction; QAction *removeAction; QAction *clearAction; KisTimeBasedItemModel *model; int lastPressSectionIndex; int calcSpanWidth(const int sectionWidth); QModelIndexList prepareFramesSlab(int startCol, int endCol); }; TimelineRulerHeader::TimelineRulerHeader(QWidget *parent) : QHeaderView(Qt::Horizontal, parent), m_d(new Private) { setSectionResizeMode(QHeaderView::Fixed); setDefaultSectionSize(18); m_d->columnEditingMenu = new QMenu(this); m_d->insertLeftAction = m_d->columnEditingMenu->addAction("Insert 1 Left", this, SLOT(slotInsertColumnLeft())); m_d->insertRightAction = m_d->columnEditingMenu->addAction("Insert 1 Right", this, SLOT(slotInsertColumnRight())); m_d->clearAction = m_d->columnEditingMenu->addAction("Clear Columns", this, SLOT(slotClearColumns())); m_d->removeAction = m_d->columnEditingMenu->addAction("Remove Columns", this, SLOT(slotRemoveColumns())); } TimelineRulerHeader::~TimelineRulerHeader() { } void TimelineRulerHeader::paintEvent(QPaintEvent *e) { QHeaderView::paintEvent(e); // Copied from Qt 4.8... if (count() == 0) return; QPainter painter(viewport()); const QPoint offset = dirtyRegionOffset(); QRect translatedEventRect = e->rect(); translatedEventRect.translate(offset); int start = -1; int end = -1; if (orientation() == Qt::Horizontal) { start = visualIndexAt(translatedEventRect.left()); end = visualIndexAt(translatedEventRect.right()); } else { start = visualIndexAt(translatedEventRect.top()); end = visualIndexAt(translatedEventRect.bottom()); } const bool reverseImpl = orientation() == Qt::Horizontal && isRightToLeft(); if (reverseImpl) { start = (start == -1 ? count() - 1 : start); end = (end == -1 ? 0 : end); } else { start = (start == -1 ? 0 : start); end = (end == -1 ? count() - 1 : end); } int tmp = start; start = qMin(start, end); end = qMax(tmp, end); /////////////////////////////////////////////////// /// Krita specific code. We should update in spans! const int spanStart = start - start % m_d->fps; const int spanEnd = end - end % m_d->fps + m_d->fps - 1; start = spanStart; end = qMin(count() - 1, spanEnd); /// End of Krita specific code /////////////////////////////////////////////////// QRect currentSectionRect; int logical; const int width = viewport()->width(); const int height = viewport()->height(); for (int i = start; i <= end; ++i) { // DK: cannot copy-paste easily... // if (d->isVisualIndexHidden(i)) // continue; painter.save(); logical = logicalIndex(i); if (orientation() == Qt::Horizontal) { currentSectionRect.setRect(sectionViewportPosition(logical), 0, sectionSize(logical), height); } else { currentSectionRect.setRect(0, sectionViewportPosition(logical), width, sectionSize(logical)); } currentSectionRect.translate(offset); QVariant variant = model()->headerData(logical, orientation(), Qt::FontRole); if (variant.isValid() && variant.canConvert()) { QFont sectionFont = qvariant_cast(variant); painter.setFont(sectionFont); } paintSection1(&painter, currentSectionRect, logical); painter.restore(); } } void TimelineRulerHeader::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const { // Base paint event should paint nothing in the sections area Q_UNUSED(painter); Q_UNUSED(rect); Q_UNUSED(logicalIndex); } void TimelineRulerHeader::paintSpan(QPainter *painter, int userFrameId, const QRect &spanRect, bool isIntegralLine, bool isPrevIntegralLine, QStyle *style, const QPalette &palette, const QPen &gridPen) const { painter->fillRect(spanRect, palette.brush(QPalette::Button)); int safeRight = spanRect.right(); QPen oldPen = painter->pen(); painter->setPen(gridPen); int adjustedTop = spanRect.top() + (!isIntegralLine ? spanRect.height() / 2 : 0); painter->drawLine(safeRight, adjustedTop, safeRight, spanRect.bottom()); if (isPrevIntegralLine) { painter->drawLine(spanRect.left() + 1, spanRect.top(), spanRect.left() + 1, spanRect.bottom()); } painter->setPen(oldPen); QString frameIdText = QString::number(userFrameId); QRect textRect(spanRect.topLeft() + QPoint(2, 0), QSize(spanRect.width() - 2, spanRect.height())); QStyleOptionHeader opt; initStyleOption(&opt); QStyle::State state = QStyle::State_None; if (isEnabled()) state |= QStyle::State_Enabled; if (window()->isActiveWindow()) state |= QStyle::State_Active; opt.state |= state; opt.selectedPosition = QStyleOptionHeader::NotAdjacent; opt.textAlignment = Qt::AlignLeft | Qt::AlignTop; opt.rect = textRect; opt.text = frameIdText; style->drawControl(QStyle::CE_HeaderLabel, &opt, painter, this); } int TimelineRulerHeader::Private::calcSpanWidth(const int sectionWidth) { const int minWidth = 36; int spanWidth = this->fps; while (spanWidth * sectionWidth < minWidth) { spanWidth *= 2; } bool splitHappened = false; do { splitHappened = false; if (!(spanWidth & 0x1) && spanWidth * sectionWidth / 2 > minWidth) { spanWidth /= 2; splitHappened = true; } else if (!(spanWidth % 3) && spanWidth * sectionWidth / 3 > minWidth) { spanWidth /= 3; splitHappened = true; } else if (!(spanWidth % 5) && spanWidth * sectionWidth / 5 > minWidth) { spanWidth /= 5; splitHappened = true; } } while (splitHappened); if (sectionWidth > minWidth) { spanWidth = 1; } return spanWidth; } void TimelineRulerHeader::paintSection1(QPainter *painter, const QRect &rect, int logicalIndex) const { if (!rect.isValid()) return; QFontMetrics metrics(this->font()); const int textHeight = metrics.height(); QPoint p1 = rect.topLeft() + QPoint(0, textHeight); QPoint p2 = rect.topRight() + QPoint(0, textHeight); QRect frameRect = QRect(p1, QSize(rect.width(), rect.height() - textHeight)); const int width = rect.width(); int spanWidth = m_d->calcSpanWidth(width); const int internalIndex = logicalIndex % spanWidth; const int userFrameId = logicalIndex; const int spanEnd = qMin(count(), logicalIndex + spanWidth); QRect spanRect(rect.topLeft(), QSize(width * (spanEnd - logicalIndex), textHeight)); - QStyleOptionViewItemV4 option = viewOptions(); + QStyleOptionViewItem option = viewOptions(); const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this); const QColor gridColor = static_cast(gridHint); const QPen gridPen = QPen(gridColor); if (!internalIndex) { bool isIntegralLine = (logicalIndex + spanWidth) % m_d->fps == 0; bool isPrevIntegralLine = logicalIndex % m_d->fps == 0; paintSpan(painter, userFrameId, spanRect, isIntegralLine, isPrevIntegralLine, style(), palette(), gridPen); } { QBrush fillColor = TimelineColorScheme::instance()->headerEmpty(); QVariant activeValue = model()->headerData(logicalIndex, orientation(), KisTimeBasedItemModel::ActiveFrameRole); QVariant cachedValue = model()->headerData(logicalIndex, orientation(), KisTimeBasedItemModel::FrameCachedRole); if (activeValue.isValid() && activeValue.toBool()) { fillColor = TimelineColorScheme::instance()->headerActive(); } else if (cachedValue.isValid() && cachedValue.toBool()) { fillColor = TimelineColorScheme::instance()->headerCachedFrame(); } painter->fillRect(frameRect, fillColor); QVector lines; lines << QLine(p1, p2); lines << QLine(frameRect.topRight(), frameRect.bottomRight()); lines << QLine(frameRect.bottomLeft(), frameRect.bottomRight()); QPen oldPen = painter->pen(); painter->setPen(gridPen); painter->drawLines(lines); painter->setPen(oldPen); } } void TimelineRulerHeader::changeEvent(QEvent *event) { Q_UNUSED(event); updateMinimumSize(); } void TimelineRulerHeader::setFramePerSecond(int fps) { m_d->fps = fps; update(); } bool TimelineRulerHeader::setZoom(qreal zoom) { const int minSectionSize = 4; const int unitSectionSize = 18; int newSectionSize = zoom * unitSectionSize; if (newSectionSize < minSectionSize) { newSectionSize = minSectionSize; zoom = qreal(newSectionSize) / unitSectionSize; } if (newSectionSize != defaultSectionSize()) { setDefaultSectionSize(newSectionSize); return true; } return false; } void TimelineRulerHeader::updateMinimumSize() { QFontMetrics metrics(this->font()); const int textHeight = metrics.height(); setMinimumSize(0, 1.5 * textHeight); } void TimelineRulerHeader::setModel(QAbstractItemModel *model) { KisTimeBasedItemModel *framesModel = qobject_cast(model); m_d->model = framesModel; QHeaderView::setModel(model); } int getColumnCount(const QModelIndexList &indexes, int *leftmostCol, int *rightmostCol) { QVector columns; int leftmost = std::numeric_limits::max(); int rightmost = std::numeric_limits::min(); Q_FOREACH (const QModelIndex &index, indexes) { leftmost = qMin(leftmost, index.column()); rightmost = qMax(rightmost, index.column()); if (!columns.contains(index.column())) { columns.append(index.column()); } } if (leftmostCol) *leftmostCol = leftmost; if (rightmostCol) *rightmostCol = rightmost; return columns.size(); } void TimelineRulerHeader::mousePressEvent(QMouseEvent *e) { int logical = logicalIndexAt(e->pos()); if (logical != -1) { QModelIndexList selectedIndexes = selectionModel()->selectedIndexes(); int numSelectedColumns = getColumnCount(selectedIndexes, 0, 0); if (e->button() == Qt::RightButton) { if (numSelectedColumns <= 1) { model()->setHeaderData(logical, orientation(), true, KisTimeBasedItemModel::ActiveFrameRole); } m_d->insertLeftAction->setText(i18n("Insert %1 left", numSelectedColumns)); m_d->insertRightAction->setText(i18n("Insert %1 right", numSelectedColumns)); m_d->clearAction->setText(i18n("Clear %1 columns", numSelectedColumns)); m_d->removeAction->setText(i18n("Remove %1 columns", numSelectedColumns)); m_d->columnEditingMenu->exec(e->globalPos()); return; } else if (e->button() == Qt::LeftButton) { m_d->lastPressSectionIndex = logical; model()->setHeaderData(logical, orientation(), true, KisTimeBasedItemModel::ActiveFrameRole); } } QHeaderView::mousePressEvent(e); } void TimelineRulerHeader::mouseMoveEvent(QMouseEvent *e) { int logical = logicalIndexAt(e->pos()); if (logical != -1) { if (e->buttons() & Qt::LeftButton) { m_d->model->setScrubState(true); model()->setHeaderData(logical, orientation(), true, KisTimeBasedItemModel::ActiveFrameRole); if (m_d->lastPressSectionIndex >= 0 && logical != m_d->lastPressSectionIndex && e->modifiers() & Qt::ShiftModifier) { const int minCol = qMin(m_d->lastPressSectionIndex, logical); const int maxCol = qMax(m_d->lastPressSectionIndex, logical); QItemSelection sel(m_d->model->index(0, minCol), m_d->model->index(0, maxCol)); selectionModel()->select(sel, QItemSelectionModel::Columns | QItemSelectionModel::SelectCurrent); } } } QHeaderView::mouseMoveEvent(e); } void TimelineRulerHeader::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { m_d->model->setScrubState(false); } QHeaderView::mouseReleaseEvent(e); } QModelIndexList TimelineRulerHeader::Private::prepareFramesSlab(int startCol, int endCol) { QModelIndexList frames; const int numRows = model->rowCount(); for (int i = 0; i < numRows; i++) { for (int j = startCol; j <= endCol; j++) { QModelIndex index = model->index(i, j); const bool exists = model->data(index, KisTimeBasedItemModel::FrameExistsRole).toBool(); if (exists) { frames << index; } } } return frames; } void TimelineRulerHeader::slotInsertColumnLeft() { QModelIndexList selectedIndexes = selectionModel()->selectedIndexes(); int leftmostCol = 0, rightmostCol = 0; int numColumns = getColumnCount(selectedIndexes, &leftmostCol, &rightmostCol); QModelIndexList movingFrames = m_d->prepareFramesSlab(leftmostCol, m_d->model->columnCount() - 1); m_d->model->offsetFrames(movingFrames, QPoint(numColumns, 0), false); } void TimelineRulerHeader::slotInsertColumnRight() { QModelIndexList selectedIndexes = selectionModel()->selectedIndexes(); int leftmostCol = 0, rightmostCol = 0; int numColumns = getColumnCount(selectedIndexes, &leftmostCol, &rightmostCol); QModelIndexList movingFrames = m_d->prepareFramesSlab(rightmostCol + 1, m_d->model->columnCount() - 1); m_d->model->offsetFrames(movingFrames, QPoint(numColumns, 0), false); } void TimelineRulerHeader::slotClearColumns(bool removeColumns) { QModelIndexList selectedIndexes = selectionModel()->selectedIndexes(); int leftmostCol = 0, rightmostCol = 0; int numColumns = getColumnCount(selectedIndexes, &leftmostCol, &rightmostCol); QModelIndexList movingFrames = m_d->prepareFramesSlab(leftmostCol, rightmostCol); m_d->model->removeFrames(movingFrames); if (removeColumns) { QModelIndexList movingFrames = m_d->prepareFramesSlab(rightmostCol + 1, m_d->model->columnCount() - 1); m_d->model->offsetFrames(movingFrames, QPoint(-numColumns, 0), false); } } void TimelineRulerHeader::slotRemoveColumns() { slotClearColumns(true); } diff --git a/plugins/extensions/gmic/kis_html_delegate.cpp b/plugins/extensions/gmic/kis_html_delegate.cpp index ac161a4645..843d3acc28 100644 --- a/plugins/extensions/gmic/kis_html_delegate.cpp +++ b/plugins/extensions/gmic/kis_html_delegate.cpp @@ -1,62 +1,62 @@ /* * Copyright (c) 2013 Lukáš Tvrdý #include #include #include #include void HtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - QStyleOptionViewItemV4 optionV4 = option; + QStyleOptionViewItem optionV4 = option; initStyleOption(&optionV4, index); QStyle *style = optionV4.widget? optionV4.widget->style() : QApplication::style(); QTextDocument doc; doc.setHtml(optionV4.text); /// Painting item without text optionV4.text = QString(); style->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter); QAbstractTextDocumentLayout::PaintContext ctx; // Highlighting text if item is selected if (optionV4.state & QStyle::State_Selected) ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText)); QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &optionV4); painter->save(); painter->translate(textRect.topLeft()); painter->setClipRect(textRect.translated(-textRect.topLeft())); doc.documentLayout()->draw(painter, ctx); painter->restore(); } QSize HtmlDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { - QStyleOptionViewItemV4 optionV4 = option; + QStyleOptionViewItem optionV4 = option; initStyleOption(&optionV4, index); QTextDocument doc; doc.setHtml(optionV4.text); doc.setTextWidth(optionV4.rect.width()); return QSize(doc.idealWidth(), doc.size().height()); } diff --git a/plugins/flake/textshape/dialogs/StylesCombo.cpp b/plugins/flake/textshape/dialogs/StylesCombo.cpp index 22c69849d1..f6e8ad7bf1 100644 --- a/plugins/flake/textshape/dialogs/StylesCombo.cpp +++ b/plugins/flake/textshape/dialogs/StylesCombo.cpp @@ -1,217 +1,217 @@ /* This file is part of the KDE project * Copyright (C) 2011-2012 Pierre Stirnweiss * * 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 "StylesCombo.h" #include #include "AbstractStylesModel.h" #include "StylesComboPreview.h" #include "StylesDelegate.h" #include #include -#include +#include #include StylesCombo::StylesCombo(QWidget *parent) : QComboBox(parent) , m_stylesModel(0) , m_view(new QListView()) , m_selectedItem(-1) , m_originalStyle(true) { // Force "Base" background to white, so the background is consistent with the one // of the preview area in the style manager. Also the usual document text colors // are dark, because made for a white paper background, so with a dark UI // color scheme they are hardly seen. // Force palette entry "Text" to black as contrast, as the pop-up button // symbol is often drawn with this palette entry // TODO: update to background color of currently selected/focused shape/page QPalette palette = this->palette(); palette.setColor(QPalette::Base, QColor(Qt::white)); palette.setColor(QPalette::Text, QColor(Qt::black)); setPalette(palette); setMinimumSize(50, 32); m_view->setMinimumWidth(250); m_view->setMouseTracking(true); setView(m_view); view()->viewport()->installEventFilter(this); StylesDelegate *delegate = new StylesDelegate(); connect(delegate, SIGNAL(needsUpdate(QModelIndex)), m_view, SLOT(update(QModelIndex))); connect(delegate, SIGNAL(styleManagerButtonClicked(QModelIndex)), this, SLOT(slotShowDia(QModelIndex))); connect(delegate, SIGNAL(deleteStyleButtonClicked(QModelIndex)), this, SLOT(slotDeleteStyle(QModelIndex))); connect(delegate, SIGNAL(clickedInItem(QModelIndex)), this, SLOT(slotItemClicked(QModelIndex))); setItemDelegate(delegate); // connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSelectionChanged(int))); QComboBox::setEditable(true); setIconSize(QSize(0, 0)); StylesComboPreview *preview = new StylesComboPreview(this); QComboBox::setEditable(true); setLineEdit(preview); } StylesCombo::~StylesCombo() { } void StylesCombo::setStyleIsOriginal(bool original) { m_originalStyle = original; if (!original) { m_preview->setAddButtonShown(true); } else { m_preview->setAddButtonShown(false); } } void StylesCombo::setStylesModel(AbstractStylesModel *model) { m_stylesModel = model; setModel(model); } void StylesCombo::setEditable(bool editable) { if (editable) { // Create a StylesComboPreview instead of a QLineEdit // Compared to QComboBox::setEditable, we might be missing the SH_ComboBox_Popup code though... // If a style needs this, then we'll need to call QComboBox::setEditable and then setLineEdit again StylesComboPreview *edit = new StylesComboPreview(this); setLineEdit(edit); } else { QComboBox::setEditable(editable); } } void StylesCombo::setLineEdit(QLineEdit *edit) { if (!isEditable() && edit && !qstrcmp(edit->metaObject()->className(), "QLineEdit")) { // uic generates code that creates a read-only StylesCombo and then // calls combo->setEditable( true ), which causes QComboBox to set up // a dumb QLineEdit instead of our nice StylesComboPreview. // As some StylesCombo features rely on the StylesComboPreview, we reject // this order here. delete edit; StylesComboPreview *preview = new StylesComboPreview(this); edit = preview; } QComboBox::setLineEdit(edit); m_preview = qobject_cast(edit); if (m_preview) { connect(m_preview, SIGNAL(resized()), this, SLOT(slotUpdatePreview())); connect(m_preview, SIGNAL(newStyleRequested(QString)), this, SIGNAL(newStyleRequested(QString))); connect(m_preview, SIGNAL(clicked()), this, SLOT(slotPreviewClicked())); } } void StylesCombo::slotSelectionChanged(int index) { m_selectedItem = index; m_preview->setPreview(m_stylesModel->stylePreview(index, m_preview->availableSize())); update(); // emit selectionChanged(index); } void StylesCombo::slotItemClicked(const QModelIndex &index) { //this slot allows us to emit a selected signal. There is a bit of redundancy if the item clicked was indeed a new selection, where we also emit the selectionChanged signal from the slot above. m_selectedItem = index.row(); m_preview->setPreview(m_stylesModel->stylePreview(m_selectedItem, m_preview->availableSize())); m_currentIndex = index; update(); emit selected(m_selectedItem); emit selected(index); hidePopup(); //the editor event has accepted the mouseReleased event. Call hidePopup ourselves then. } void StylesCombo::slotUpdatePreview() { if (!m_stylesModel) { return; } m_preview->setPreview(m_stylesModel->stylePreview(currentIndex(), m_preview->availableSize())); update(); } void StylesCombo::slotPreviewClicked() { if (!view()->isVisible()) { showPopup(); } } bool StylesCombo::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::MouseButtonRelease && object == view()->viewport()) { QMouseEvent *mouseEvent = static_cast(event); //If what follows isn't a HACK then I have no clue what is!!!! //The item delegate editorEvent method is not sent MouseButtonRelease events. //This is because the QComboBox installs an event filter on the view and calls //popup->hide() on MouseButtonRelease to dismiss the view. Since we installed an event filter on the view //ourselves, we can prevent hiding the popup. We have to call itemDelegate->editorEvent //manually though. QModelIndex index = view()->indexAt(mouseEvent->pos()); QModelIndex buddy = m_stylesModel->buddy(index); - QStyleOptionViewItemV4 options; + QStyleOptionViewItem options; options.rect = view()->visualRect(buddy); options.widget = m_view; options.state |= (buddy == view()->currentIndex() ? QStyle::State_HasFocus : QStyle::State_None); return view()->itemDelegate()->editorEvent(mouseEvent, m_stylesModel, options, index); } return false; } void StylesCombo::slotShowDia(const QModelIndex &index) { emit showStyleManager(index.row()); } void StylesCombo::slotDeleteStyle(const QModelIndex &index) { emit deleteStyle(index.row()); } void StylesCombo::slotModelReset() { m_view->reset(); } void StylesCombo::showEditIcon(bool show) { StylesDelegate *delegate = dynamic_cast(itemDelegate()); Q_ASSERT(delegate); if (!delegate) { //the following should never get called as we are creating a StylesDelegate on the constructor; StylesDelegate *delegate = new StylesDelegate(); connect(delegate, SIGNAL(needsUpdate(QModelIndex)), m_view, SLOT(update(QModelIndex))); connect(delegate, SIGNAL(styleManagerButtonClicked(QModelIndex)), this, SLOT(slotShowDia(QModelIndex))); connect(delegate, SIGNAL(deleteStyleButtonClicked(QModelIndex)), this, SLOT(slotDeleteStyle(QModelIndex))); connect(delegate, SIGNAL(clickedInItem(QModelIndex)), this, SLOT(slotItemClicked(QModelIndex))); setItemDelegate(delegate); } delegate->setEditButtonEnable(show); } diff --git a/plugins/flake/textshape/dialogs/StylesDelegate.cpp b/plugins/flake/textshape/dialogs/StylesDelegate.cpp index 7112dd2732..3dcf5defd8 100644 --- a/plugins/flake/textshape/dialogs/StylesDelegate.cpp +++ b/plugins/flake/textshape/dialogs/StylesDelegate.cpp @@ -1,303 +1,303 @@ /* This file is part of the KDE project * Copyright (C) 2011 C. Boemann * Copyright (C) 2011-2012 Pierre Stirnweiss * * 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 "StylesDelegate.h" #include "AbstractStylesModel.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include StylesDelegate::StylesDelegate() : QStyledItemDelegate() , m_editButtonPressed(false) , m_deleteButtonPressed(false) , m_enableEditButton(true) { m_buttonSize = 16; m_buttonDistance = 2; } void StylesDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optionV1, const QModelIndex &index) const { - QStyleOptionViewItemV4 option = optionV1; + QStyleOptionViewItem option = optionV1; initStyleOption(&option, index); if (!index.data(AbstractStylesModel::isTitleRole).toBool()) { QStyledItemDelegate::paint(painter, option, index); //the following is needed to find out if the view has vertical scrollbars. If there is no view just paint and do not attempt to draw the control buttons. //this is needed because it seems that the option.rect given does not exclude the vertical scrollBar. This means that we can draw the button in an area that is going to be covered by the vertical scrollBar. const QAbstractItemView *view = static_cast(option.widget); if (!view) { return; } QScrollBar *scrollBar = view->verticalScrollBar(); int scrollBarWidth = 0; if (scrollBar->isVisible()) { scrollBarWidth = scrollBar->width(); } if (!index.isValid() || !(option.state & QStyle::State_MouseOver)) { return; } // Delete style button. int dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - m_buttonSize - m_buttonDistance - 2; int dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; int dx2 = -m_buttonSize - m_buttonDistance - 2; int dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; /* TODO: when we can safely delete styles, re-enable this QStyleOptionButton optDel; if (!m_deleteButtonPressed) { optDel.state |= QStyle::State_Enabled; } optDel.icon = koIcon("edit-delete"); optDel.features |= QStyleOptionButton::Flat; optDel.rect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); view->style()->drawControl(QStyle::CE_PushButton, &optDel, painter, 0); */ // Open style manager dialog button. if (!m_enableEditButton) { // when we don't want edit icon return; } dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - 2; dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; dx2 = -2; dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; QStyleOptionButton optEdit; if (!m_editButtonPressed) { optEdit.state |= QStyle::State_Enabled; } optEdit.icon = koIcon("configure"); optEdit.features |= QStyleOptionButton::Flat; optEdit.rect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); view->style()->drawControl(QStyle::CE_PushButton, &optEdit, painter, 0); } else { const QString category = index.data().toString(); const QRect optRect = option.rect; QFont font(QApplication::font()); font.setBold(true); const QFontMetrics fontMetrics = QFontMetrics(font); QColor outlineColor = option.palette.text().color(); outlineColor.setAlphaF(0.35); //BEGIN: top left corner { painter->save(); painter->setPen(outlineColor); const QPointF topLeft(optRect.topLeft()); QRectF arc(topLeft, QSizeF(4, 4)); arc.translate(0.5, 0.5); painter->drawArc(arc, 1440, 1440); painter->restore(); } //END: top left corner //BEGIN: left vertical line { QPoint start(optRect.topLeft()); start.ry() += 3; QPoint verticalGradBottom(optRect.topLeft()); verticalGradBottom.ry() += fontMetrics.height() + 5; QLinearGradient gradient(start, verticalGradBottom); gradient.setColorAt(0, outlineColor); gradient.setColorAt(1, Qt::transparent); painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); } //END: left vertical line //BEGIN: horizontal line { QPoint start(optRect.topLeft()); start.rx() += 3; QPoint horizontalGradTop(optRect.topLeft()); horizontalGradTop.rx() += optRect.width() - 6; painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); } //END: horizontal line //BEGIN: top right corner { painter->save(); painter->setPen(outlineColor); QPointF topRight(optRect.topRight()); topRight.rx() -= 4; QRectF arc(topRight, QSizeF(4, 4)); arc.translate(0.5, 0.5); painter->drawArc(arc, 0, 1440); painter->restore(); } //END: top right corner //BEGIN: right vertical line { QPoint start(optRect.topRight()); start.ry() += 3; QPoint verticalGradBottom(optRect.topRight()); verticalGradBottom.ry() += fontMetrics.height() + 5; QLinearGradient gradient(start, verticalGradBottom); gradient.setColorAt(0, outlineColor); gradient.setColorAt(1, Qt::transparent); painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); } //END: right vertical line //BEGIN: text { QRect textRect(option.rect); textRect.setTop(textRect.top() + 7); textRect.setLeft(textRect.left() + 7); textRect.setHeight(fontMetrics.height()); textRect.setRight(textRect.right() - 7); painter->save(); painter->setFont(font); QColor penColor(option.palette.text().color()); penColor.setAlphaF(0.6); painter->setPen(penColor); painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category); painter->restore(); } //END: text } } QSize StylesDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); return index.data(Qt::SizeHintRole).toSize(); } bool StylesDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &optionV1, const QModelIndex &index) { Q_UNUSED(model); - QStyleOptionViewItemV4 option = optionV1; + QStyleOptionViewItem option = optionV1; initStyleOption(&option, index); //the following is needed to find out if the view has vertical scrollbars. If not just paint and do not attempt to draw the control buttons. //this is needed because it seems that the option.rect given does not exclude the vertical scrollBar. This means that we can draw the button in an area that is going to be covered by the vertical scrollBar. const QAbstractItemView *view = static_cast(option.widget); if (!view) { return false; } QScrollBar *scrollBar = view->verticalScrollBar(); int scrollBarWidth = 0; if (scrollBar->isVisible()) { scrollBarWidth = scrollBar->width(); } if (event->type() == QEvent::MouseButtonPress) { QMouseEvent *mouseEvent = static_cast(event); int dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - m_buttonSize - m_buttonDistance - 2; int dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; int dx2 = - m_buttonSize - m_buttonDistance - 2; int dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; /*TODO: when we can safely delete styles, re-enable this QRect delRect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); if (delRect.contains(mouseEvent->pos())) { m_deleteButtonPressed = true; } else { m_deleteButtonPressed = false; } */ dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - 2; dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; dx2 = -2; dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; QRect editRect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); if (editRect.contains(mouseEvent->pos())) { m_editButtonPressed = true; } else { m_editButtonPressed = false; } emit needsUpdate(index); } if (event->type() == QEvent::MouseButtonRelease) { m_deleteButtonPressed = false; m_editButtonPressed = false; emit needsUpdate(index); if (index.flags() == Qt::NoItemFlags) { //if the item is NoItemFlagged, it means it is a separator in the view. In that case, we should not close the combo's drop down. return true; } QMouseEvent *mouseEvent = static_cast(event); int dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - m_buttonSize - m_buttonDistance - 2; int dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; int dx2 = - m_buttonSize - m_buttonDistance - 2; int dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; /*TODO: when we can safely delete styles, re-enable this QRect delRect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); if (delRect.contains(mouseEvent->pos())) { emit deleteStyleButtonClicked(index); return true; } */ dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - 2; dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; dx2 = -2; dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; QRect editRect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); if (editRect.contains(mouseEvent->pos())) { emit styleManagerButtonClicked(index); return true; } emit clickedInItem(index); return true; //returning true here means the QComboBox mouseRelease code will not get called. The effect of it is that hidePopup will not get called. StylesCombo calls it in the corresponding slot. } if (event->type() == QEvent::MouseMove) { QMouseEvent *mouseEvent = static_cast(event); int dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - m_buttonSize - m_buttonDistance - 2; int dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; int dx2 = - m_buttonSize - m_buttonDistance - 2; int dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; /*TODO: when we can safely delete styles, re-enable this QRect delRect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); if (!delRect.contains(mouseEvent->pos())) { m_deleteButtonPressed = false; } */ dx1 = option.rect.width() - qMin(option.rect.height() - 2, m_buttonSize) - 2; dy1 = 1 + (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; dx2 = -2; dy2 = -1 - (option.rect.height() - qMin(option.rect.height(), m_buttonSize)) / 2; QRect editRect = option.rect.adjusted(dx1 - scrollBarWidth, dy1, dx2 - scrollBarWidth, dy2); if (!editRect.contains(mouseEvent->pos())) { m_editButtonPressed = false; } emit needsUpdate(index); return false; } return false; } void StylesDelegate::setEditButtonEnable(bool enable) { m_enableEditButton = enable; }