1
/* This file is part of the KDE libraries and the Kate part.
3
* Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Library General Public
7
* License as published by the Free Software Foundation; either
8
* version 2 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Library General Public License for more details.
15
* You should have received a copy of the GNU Library General Public License
16
* along with this library; see the file COPYING.LIB. If not, write to
17
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
* Boston, MA 02110-1301, USA.
21
#include "expandingdelegate.h"
23
#include <QtGui/QTextLine>
24
#include <QtGui/QPainter>
25
#include <QtGui/QBrush>
28
#include <QApplication>
32
#include "expandingwidgetmodel.h"
34
ExpandingDelegate::ExpandingDelegate(ExpandingWidgetModel* model, QObject* parent)
35
: QItemDelegate(parent)
40
//Gets the background-color in the way QItemDelegate does it
41
static QColor getUsedBackgroundColor(const QStyleOptionViewItem & option, const QModelIndex& index) {
42
if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) {
43
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
44
? QPalette::Normal : QPalette::Disabled;
45
if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
46
cg = QPalette::Inactive;
48
return option.palette.brush(cg, QPalette::Highlight).color();
50
QVariant value = index.data(Qt::BackgroundRole);
51
if (qVariantCanConvert<QBrush>(value))
52
return qvariant_cast<QBrush>(value).color();
55
return QApplication::palette().base().color();
58
static void dampColors(QColor& col) {
59
//Reduce the colors that are less visible to the eye, because they are closer to black when it comes to contrast
60
//The most significant color to the eye is green. Then comes red, and then blue, with blue _much_ less significant.
63
col.setRed(col.red() / 2);
66
//A hack to compute more eye-focused contrast values
67
static double readabilityContrast(QColor foreground, QColor background) {
68
dampColors(foreground);
69
dampColors(background);
70
return abs(foreground.green()-background.green()) + abs(foreground.red()-background.red()) + abs(foreground.blue() - background.blue());
73
void ExpandingDelegate::paint( QPainter * painter, const QStyleOptionViewItem & optionOld, const QModelIndex & index ) const
75
QStyleOptionViewItem option(optionOld);
77
m_currentIndex = index;
79
adjustStyle(index, option);
81
if( index.column() == 0 )
82
model()->placeExpandingWidget(index);
84
//Make sure the decorations are painted at the top, because the center of expanded items will be filled with the embedded widget.
85
if( model()->isPartiallyExpanded(index) == ExpandingWidgetModel::ExpandUpwards )
86
m_cachedAlignment = Qt::AlignBottom;
88
m_cachedAlignment = Qt::AlignTop;
90
option.decorationAlignment = m_cachedAlignment;
91
option.displayAlignment = m_cachedAlignment;
93
//kDebug( 13035 ) << "Painting row " << index.row() << ", column " << index.column() << ", internal " << index.internalPointer() << ", drawselected " << option.showDecorationSelected << ", selected " << (option.state & QStyle::State_Selected);
95
m_cachedHighlights.clear();
96
m_backgroundColor = getUsedBackgroundColor(option, index);
98
if (model()->indexIsItem(index) ) {
99
m_currentColumnStart = 0;
100
m_cachedHighlights = createHighlighting(index, option);
103
/*kDebug( 13035 ) << "Highlights for line:";
104
foreach (const QTextLayout::FormatRange& fr, m_cachedHighlights)
105
kDebug( 13035 ) << fr.start << " len " << fr.length << " format ";*/
107
QItemDelegate::paint(painter, option, index);
109
///This is a bug workaround for the Qt raster paint engine: It paints over widgets embedded into the viewport when updating due to mouse events
110
///@todo report to Qt Software
111
if( model()->isExpanded(index) && model()->expandingWidget( index ) )
112
model()->expandingWidget( index )->update();
115
QList<QTextLayout::FormatRange> ExpandingDelegate::createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const {
118
return QList<QTextLayout::FormatRange>();
121
QSize ExpandingDelegate::basicSizeHint( const QModelIndex& index ) const {
122
return QItemDelegate::sizeHint( QStyleOptionViewItem(), index );
125
QSize ExpandingDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
127
QSize s = QItemDelegate::sizeHint( option, index );
128
if( model()->isExpanded(index) && model()->expandingWidget( index ) )
130
QWidget* widget = model()->expandingWidget( index );
131
QSize widgetSize = widget->size();
133
s.setHeight( widgetSize.height() + s.height() + 10 ); //10 is the sum that must match exactly the offsets used in ExpandingWidgetModel::placeExpandingWidgets
134
} else if( model()->isPartiallyExpanded( index ) ) {
135
s.setHeight( s.height() + 30 + 10 );
140
void ExpandingDelegate::adjustStyle( const QModelIndex& index, QStyleOptionViewItem & option ) const
146
void ExpandingDelegate::adjustRect(QRect& rect) const {
147
if (!model()->indexIsItem(m_currentIndex) /*&& m_currentIndex.column() == 0*/) {
149
rect.setLeft(model()->treeView()->columnViewportPosition(0));
150
int columnCount = model()->columnCount(m_currentIndex.parent());
154
rect.setRight(model()->treeView()->columnViewportPosition(columnCount-1) + model()->treeView()->columnWidth(columnCount-1));
158
void ExpandingDelegate::drawDisplay( QPainter * painter, const QStyleOptionViewItem & option, const QRect & _rect, const QString & text ) const
164
QTextLayout layout(text, option.font, painter->device());
166
QList<QTextLayout::FormatRange> additionalFormats;
168
int missingFormats = text.length();
170
for (int i = 0; i < m_cachedHighlights.count(); ++i) {
171
if (m_cachedHighlights[i].start + m_cachedHighlights[i].length <= m_currentColumnStart)
174
if (!additionalFormats.count())
175
if (i != 0 && m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length > m_currentColumnStart) {
176
QTextLayout::FormatRange before;
178
before.length = m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length - m_currentColumnStart;
179
before.format = m_cachedHighlights[i - 1].format;
180
additionalFormats.append(before);
184
QTextLayout::FormatRange format;
185
format.start = m_cachedHighlights[i].start - m_currentColumnStart;
186
format.length = m_cachedHighlights[i].length;
187
format.format = m_cachedHighlights[i].format;
189
additionalFormats.append(format);
191
if(!additionalFormats.isEmpty())
192
missingFormats = text.length() - (additionalFormats.back().length + additionalFormats.back().start);
194
if (missingFormats > 0) {
195
QTextLayout::FormatRange format;
196
format.start = text.length() - missingFormats;
197
format.length = missingFormats;
199
fm.setForeground(option.palette.text());
201
additionalFormats.append(format);
204
if(m_backgroundColor.isValid()) {
205
QColor background = m_backgroundColor;
206
// kDebug() << text << "background:" << background.name();
207
//Now go through the formats, and make sure the contrast background/foreground is readable
208
for(int a = 0; a < additionalFormats.size(); ++a) {
209
QColor currentBackground = background;
210
if(additionalFormats[a].format.hasProperty(QTextFormat::BackgroundBrush))
211
currentBackground = additionalFormats[a].format.background().color();
213
QColor currentColor = additionalFormats[a].format.foreground().color();
215
double currentContrast = readabilityContrast(currentColor, currentBackground);
216
QColor invertedColor(0xffffffff-additionalFormats[a].format.foreground().color().rgb());
217
double invertedContrast = readabilityContrast(invertedColor, currentBackground);
219
// kDebug() << "values:" << invertedContrast << currentContrast << invertedColor.name() << currentColor.name();
221
if(invertedContrast > currentContrast) {
222
// kDebug() << text << additionalFormats[a].length << "switching from" << currentColor.name() << "to" << invertedColor.name();
223
QBrush b(additionalFormats[a].format.foreground());
224
b.setColor(invertedColor);
225
additionalFormats[a].format.setForeground(b);
230
for(int a = additionalFormats.size()-1; a >= 0; --a) {
231
if(additionalFormats[a].length == 0){
232
additionalFormats.removeAt(a);
234
///For some reason the text-formats seem to be invalid in some way, sometimes
235
///@todo Fix this properly, it sucks not copying everything over
237
fm.setForeground(QBrush(additionalFormats[a].format.foreground().color()));
238
fm.setBackground(additionalFormats[a].format.background());
239
fm.setUnderlineStyle( additionalFormats[a].format.underlineStyle() );
240
fm.setUnderlineColor( additionalFormats[a].format.underlineColor() );
241
fm.setFontWeight( additionalFormats[a].format.fontWeight() );
242
additionalFormats[a].format = fm;
246
// kDebug( 13035 ) << "Highlights for text [" << text << "] col start " << m_currentColumnStart << ":";
247
// foreach (const QTextLayout::FormatRange& fr, additionalFormats)
248
// kDebug( 13035 ) << fr.start << " len " << fr.length << "foreground" << fr.format.foreground() << "background" << fr.format.background();
250
layout.setAdditionalFormats(additionalFormats);
254
to.setAlignment( static_cast<Qt::Alignment>(m_cachedAlignment | option.displayAlignment) );
256
to.setWrapMode(QTextOption::WrapAnywhere);
257
layout.setTextOption(to);
259
layout.beginLayout();
260
QTextLine line = layout.createLine();
261
// Leave some extra space when the text is right-aligned
262
line.setLineWidth(rect.width() - (option.displayAlignment == Qt::AlignRight ? 8 : 0));
265
//We need to do some hand layouting here
266
if( to.alignment() & Qt::AlignBottom)
267
layout.draw(painter, QPoint(rect.left(), rect.bottom() - (int)line.height()) );
269
layout.draw(painter, rect.topLeft() );
273
//if (painter->fontMetrics().width(text) > textRect.width() && !text.contains(QLatin1Char('\n')))
274
//str = elidedText(option.fontMetrics, textRect.width(), option.textElideMode, text);
275
//qt_format_text(option.font, textRect, option.displayAlignment, str, 0, 0, 0, 0, painter);
278
void ExpandingDelegate::drawDecoration(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QPixmap& pixmap) const {
279
if (model()->indexIsItem(m_currentIndex) )
280
QItemDelegate::drawDecoration(painter, option, rect, pixmap);
283
void ExpandingDelegate::drawBackground ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const {
285
QStyleOptionViewItemV4 opt = option;
286
//initStyleOption(&opt, index);
287
//Problem: This isn't called at all, because drawBrackground is not virtual :-/
288
QStyle *style = model()->treeView()->style() ? model()->treeView()->style() : QApplication::style();
289
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
292
ExpandingWidgetModel* ExpandingDelegate::model() const {
296
void ExpandingDelegate::heightChanged() const {
299
bool ExpandingDelegate::editorEvent ( QEvent * event, QAbstractItemModel * /*model*/, const QStyleOptionViewItem & /*option*/, const QModelIndex & index )
301
if( event->type() == QEvent::MouseButtonRelease )
304
model()->setExpanded(index, !model()->isExpanded( index ));
315
QList<QTextLayout::FormatRange> ExpandingDelegate::highlightingFromVariantList(const QList<QVariant>& customHighlights) const
317
QList<QTextLayout::FormatRange> ret;
319
for (int i = 0; i + 2 < customHighlights.count(); i += 3) {
320
if (!customHighlights[i].canConvert(QVariant::Int) || !customHighlights[i+1].canConvert(QVariant::Int) || !customHighlights[i+2].canConvert<QTextFormat>()) {
321
kWarning() << "Unable to convert triple to custom formatting.";
325
QTextLayout::FormatRange format;
326
format.start = customHighlights[i].toInt();
327
format.length = customHighlights[i+1].toInt();
328
format.format = customHighlights[i+2].value<QTextFormat>().toCharFormat();
330
if(!format.format.isValid())
331
kWarning() << "Format is not valid";
338
#include "expandingdelegate.moc"