Kate
expandingdelegate.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries and the Kate part. 00002 * 00003 * Copyright (C) 2006 Hamish Rodda <rodda@kde.org> 00004 * 00005 * This library is free software; you can redistribute it and/or 00006 * modify it under the terms of the GNU Library General Public 00007 * License as published by the Free Software Foundation; either 00008 * version 2 of the License, or (at your option) any later version. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "expandingdelegate.h" 00022 00023 #include <QtGui/QTextLine> 00024 #include <QtGui/QPainter> 00025 #include <QtGui/QBrush> 00026 #include <QKeyEvent> 00027 #include <QTreeView> 00028 #include <QApplication> 00029 00030 #include <kdebug.h> 00031 00032 #include "expandingwidgetmodel.h" 00033 00034 ExpandingDelegate::ExpandingDelegate(ExpandingWidgetModel* model, QObject* parent) 00035 : QItemDelegate(parent) 00036 , m_model(model) 00037 { 00038 } 00039 00040 //Gets the background-color in the way QItemDelegate does it 00041 static QColor getUsedBackgroundColor(const QStyleOptionViewItem & option, const QModelIndex& index) { 00042 if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) { 00043 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled 00044 ? QPalette::Normal : QPalette::Disabled; 00045 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) 00046 cg = QPalette::Inactive; 00047 00048 return option.palette.brush(cg, QPalette::Highlight).color(); 00049 } else { 00050 QVariant value = index.data(Qt::BackgroundRole); 00051 if (qVariantCanConvert<QBrush>(value)) 00052 return qvariant_cast<QBrush>(value).color(); 00053 } 00054 00055 return QApplication::palette().background().color(); 00056 } 00057 00058 static void dampColors(QColor& col) { 00059 //Reduce the colors that are less visible to the eye, because they are closer to black when it comes to contrast 00060 //The most significant color to the eye is green. Then comes red, and then blue, with blue _much_ less significant. 00061 00062 col.setBlue(0); 00063 col.setRed(col.red() / 2); 00064 } 00065 00066 //A hack to compute more eye-focused contrast values 00067 static double readabilityContrast(QColor foreground, QColor background) { 00068 dampColors(foreground); 00069 dampColors(background); 00070 return abs(foreground.green()-background.green()) + abs(foreground.red()-background.red()) + abs(foreground.blue() - background.blue()); 00071 } 00072 00073 void ExpandingDelegate::paint( QPainter * painter, const QStyleOptionViewItem & optionOld, const QModelIndex & index ) const 00074 { 00075 QStyleOptionViewItem option(optionOld); 00076 00077 m_currentIndex = index; 00078 00079 adjustStyle(index, option); 00080 00081 if( index.column() == 0 ) 00082 model()->placeExpandingWidget(index); 00083 00084 //Make sure the decorations are painted at the top, because the center of expanded items will be filled with the embedded widget. 00085 if( model()->isPartiallyExpanded(index) == ExpandingWidgetModel::ExpandUpwards ) 00086 m_cachedAlignment = Qt::AlignBottom; 00087 else 00088 m_cachedAlignment = Qt::AlignTop; 00089 00090 option.decorationAlignment = m_cachedAlignment; 00091 option.displayAlignment = m_cachedAlignment; 00092 00093 //kDebug( 13035 ) << "Painting row " << index.row() << ", column " << index.column() << ", internal " << index.internalPointer() << ", drawselected " << option.showDecorationSelected << ", selected " << (option.state & QStyle::State_Selected); 00094 00095 m_cachedHighlights.clear(); 00096 m_backgroundColor = getUsedBackgroundColor(option, index); 00097 00098 if (model()->indexIsItem(index) ) { 00099 m_currentColumnStart = 0; 00100 m_cachedHighlights = createHighlighting(index, option); 00101 } 00102 00103 /*kDebug( 13035 ) << "Highlights for line:"; 00104 foreach (const QTextLayout::FormatRange& fr, m_cachedHighlights) 00105 kDebug( 13035 ) << fr.start << " len " << fr.length << " format ";*/ 00106 00107 QItemDelegate::paint(painter, option, index); 00108 00111 if( model()->isExpanded(index) && model()->expandingWidget( index ) ) 00112 model()->expandingWidget( index )->update(); 00113 } 00114 00115 QList<QTextLayout::FormatRange> ExpandingDelegate::createHighlighting(const QModelIndex& index, QStyleOptionViewItem& option) const { 00116 Q_UNUSED( index ); 00117 Q_UNUSED( option ); 00118 return QList<QTextLayout::FormatRange>(); 00119 } 00120 00121 QSize ExpandingDelegate::basicSizeHint( const QModelIndex& index ) const { 00122 return QItemDelegate::sizeHint( QStyleOptionViewItem(), index ); 00123 } 00124 00125 QSize ExpandingDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const 00126 { 00127 QSize s = QItemDelegate::sizeHint( option, index ); 00128 if( model()->isExpanded(index) && model()->expandingWidget( index ) ) 00129 { 00130 QWidget* widget = model()->expandingWidget( index ); 00131 QSize widgetSize = widget->size(); 00132 00133 s.setHeight( widgetSize.height() + s.height() + 10 ); //10 is the sum that must match exactly the offsets used in ExpandingWidgetModel::placeExpandingWidgets 00134 } else if( model()->isPartiallyExpanded( index ) ) { 00135 s.setHeight( s.height() + 30 + 10 ); 00136 } 00137 return s; 00138 } 00139 00140 void ExpandingDelegate::adjustStyle( const QModelIndex& index, QStyleOptionViewItem & option ) const 00141 { 00142 Q_UNUSED(index) 00143 Q_UNUSED(option) 00144 } 00145 00146 void ExpandingDelegate::adjustRect(QRect& rect) const { 00147 if (!model()->indexIsItem(m_currentIndex) /*&& m_currentIndex.column() == 0*/) { 00148 00149 rect.setLeft(model()->treeView()->columnViewportPosition(0)); 00150 int columnCount = model()->columnCount(m_currentIndex.parent()); 00151 00152 if(!columnCount) 00153 return; 00154 rect.setRight(model()->treeView()->columnViewportPosition(columnCount-1) + model()->treeView()->columnWidth(columnCount-1)); 00155 } 00156 } 00157 00158 void ExpandingDelegate::drawDisplay( QPainter * painter, const QStyleOptionViewItem & option, const QRect & _rect, const QString & text ) const 00159 { 00160 QRect rect(_rect); 00161 00162 adjustRect(rect); 00163 00164 QTextLayout layout(text, option.font, painter->device()); 00165 00166 QRect textRect = rect.adjusted(1, 0, -1, 0); // remove width padding 00167 00168 QList<QTextLayout::FormatRange> additionalFormats; 00169 00170 int missingFormats = text.length(); 00171 00172 for (int i = 0; i < m_cachedHighlights.count(); ++i) { 00173 if (m_cachedHighlights[i].start + m_cachedHighlights[i].length <= m_currentColumnStart) 00174 continue; 00175 00176 if (!additionalFormats.count()) 00177 if (i != 0 && m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length > m_currentColumnStart) { 00178 QTextLayout::FormatRange before; 00179 before.start = 0; 00180 before.length = m_cachedHighlights[i - 1].start + m_cachedHighlights[i - 1].length - m_currentColumnStart; 00181 before.format = m_cachedHighlights[i - 1].format; 00182 additionalFormats.append(before); 00183 } 00184 00185 00186 QTextLayout::FormatRange format; 00187 format.start = m_cachedHighlights[i].start - m_currentColumnStart; 00188 format.length = m_cachedHighlights[i].length; 00189 format.format = m_cachedHighlights[i].format; 00190 00191 additionalFormats.append(format); 00192 } 00193 if(!additionalFormats.isEmpty()) 00194 missingFormats = text.length() - (additionalFormats.back().length + additionalFormats.back().start); 00195 00196 if (missingFormats > 0) { 00197 QTextLayout::FormatRange format; 00198 format.start = text.length() - missingFormats; 00199 format.length = missingFormats; 00200 QTextCharFormat fm; 00201 fm.setForeground(option.palette.text()); 00202 format.format = fm; 00203 additionalFormats.append(format); 00204 } 00205 00206 if(m_backgroundColor.isValid()) { 00207 QColor background = m_backgroundColor; 00208 // kDebug() << text << "background:" << background.name(); 00209 //Now go through the formats, and make sure the contrast background/foreground is readable 00210 for(int a = 0; a < additionalFormats.size(); ++a) { 00211 QColor currentBackground = background; 00212 if(additionalFormats[a].format.hasProperty(QTextFormat::BackgroundBrush)) 00213 currentBackground = additionalFormats[a].format.background().color(); 00214 00215 QColor currentColor = additionalFormats[a].format.foreground().color(); 00216 00217 double currentContrast = readabilityContrast(currentColor, currentBackground); 00218 QColor invertedColor(0xffffffff-additionalFormats[a].format.foreground().color().rgb()); 00219 double invertedContrast = readabilityContrast(invertedColor, currentBackground); 00220 00221 // kDebug() << "values:" << invertedContrast << currentContrast << invertedColor.name() << currentColor.name(); 00222 00223 if(invertedContrast > currentContrast) { 00224 // kDebug() << text << additionalFormats[a].length << "switching from" << currentColor.name() << "to" << invertedColor.name(); 00225 QBrush b(additionalFormats[a].format.foreground()); 00226 b.setColor(invertedColor); 00227 additionalFormats[a].format.setForeground(b); 00228 } 00229 } 00230 } 00231 00232 for(int a = additionalFormats.size()-1; a >= 0; --a) { 00233 if(additionalFormats[a].length == 0){ 00234 additionalFormats.removeAt(a); 00235 }else{ 00238 QTextCharFormat fm; 00239 fm.setForeground(QBrush(additionalFormats[a].format.foreground().color())); 00240 fm.setBackground(additionalFormats[a].format.background()); 00241 fm.setUnderlineStyle( additionalFormats[a].format.underlineStyle() ); 00242 fm.setUnderlineColor( additionalFormats[a].format.underlineColor() ); 00243 fm.setFontWeight( additionalFormats[a].format.fontWeight() ); 00244 additionalFormats[a].format = fm; 00245 } 00246 } 00247 00248 // kDebug( 13035 ) << "Highlights for text [" << text << "] col start " << m_currentColumnStart << ":"; 00249 // foreach (const QTextLayout::FormatRange& fr, additionalFormats) 00250 // kDebug( 13035 ) << fr.start << " len " << fr.length << "foreground" << fr.format.foreground() << "background" << fr.format.background(); 00251 00252 layout.setAdditionalFormats(additionalFormats); 00253 00254 QTextOption to; 00255 00256 to.setAlignment( m_cachedAlignment ); 00257 00258 to.setWrapMode(QTextOption::WrapAnywhere); 00259 layout.setTextOption(to); 00260 00261 layout.beginLayout(); 00262 QTextLine line = layout.createLine(); 00263 line.setLineWidth(rect.width()); 00264 layout.endLayout(); 00265 00266 //We need to do some hand layouting here 00267 if( to.alignment() & Qt::AlignBottom) 00268 layout.draw(painter, QPoint(rect.left(), rect.bottom() - (int)line.height()) ); 00269 else 00270 layout.draw(painter, rect.topLeft() ); 00271 00272 return; 00273 00274 //if (painter->fontMetrics().width(text) > textRect.width() && !text.contains(QLatin1Char('\n'))) 00275 //str = elidedText(option.fontMetrics, textRect.width(), option.textElideMode, text); 00276 //qt_format_text(option.font, textRect, option.displayAlignment, str, 0, 0, 0, 0, painter); 00277 } 00278 00279 void ExpandingDelegate::drawDecoration(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QPixmap& pixmap) const { 00280 if (model()->indexIsItem(m_currentIndex) ) 00281 QItemDelegate::drawDecoration(painter, option, rect, pixmap); 00282 } 00283 00284 void ExpandingDelegate::drawBackground ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { 00285 Q_UNUSED(index) 00286 QStyleOptionViewItemV4 opt = option; 00287 //initStyleOption(&opt, index); 00288 //Problem: This isn't called at all, because drawBrackground is not virtual :-/ 00289 QStyle *style = model()->treeView()->style() ? model()->treeView()->style() : QApplication::style(); 00290 style->drawControl(QStyle::CE_ItemViewItem, &opt, painter); 00291 } 00292 00293 ExpandingWidgetModel* ExpandingDelegate::model() const { 00294 return m_model; 00295 } 00296 00297 void ExpandingDelegate::heightChanged() const { 00298 } 00299 00300 bool ExpandingDelegate::editorEvent ( QEvent * event, QAbstractItemModel * /*model*/, const QStyleOptionViewItem & /*option*/, const QModelIndex & index ) 00301 { 00302 QKeyEvent* keyEvent = 0; 00303 if( event->type() == QEvent::KeyPress ) 00304 keyEvent = reinterpret_cast<QKeyEvent*>(event); 00305 00306 if( event->type() == QEvent::MouseButtonRelease ) 00307 { 00308 event->accept(); 00309 model()->setExpanded(index, !model()->isExpanded( index )); 00310 heightChanged(); 00311 00312 return true; 00313 } else { 00314 event->ignore(); 00315 } 00316 00317 return false; 00318 } 00319 00320 QList<QTextLayout::FormatRange> ExpandingDelegate::highlightingFromVariantList(const QList<QVariant>& customHighlights) const 00321 { 00322 QList<QTextLayout::FormatRange> ret; 00323 00324 for (int i = 0; i + 2 < customHighlights.count(); i += 3) { 00325 if (!customHighlights[i].canConvert(QVariant::Int) || !customHighlights[i+1].canConvert(QVariant::Int) || !customHighlights[i+2].canConvert<QTextFormat>()) { 00326 kWarning() << "Unable to convert triple to custom formatting."; 00327 continue; 00328 } 00329 00330 QTextLayout::FormatRange format; 00331 format.start = customHighlights[i].toInt(); 00332 format.length = customHighlights[i+1].toInt(); 00333 format.format = customHighlights[i+2].value<QTextFormat>().toCharFormat(); 00334 00335 if(!format.format.isValid()) 00336 kWarning() << "Format is not valid"; 00337 00338 ret << format; 00339 } 00340 return ret; 00341 } 00342 00343 #include "expandingdelegate.moc"
KDE 4.6 API Reference