• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

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"

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal