00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "expandingwidgetmodel.h"
00020
00021 #include <QTreeView>
00022 #include <QModelIndex>
00023 #include <QBrush>
00024
00025 #include <ktexteditor/codecompletionmodel.h>
00026 #include <kiconloader.h>
00027 #include <ktextedit.h>
00028 #include "kcolorutils.h"
00029
00030 #include "expandingdelegate.h"
00031 #include <qapplication.h>
00032
00033 QIcon ExpandingWidgetModel::m_expandedIcon;
00034 QIcon ExpandingWidgetModel::m_collapsedIcon;
00035
00036 using namespace KTextEditor;
00037
00038 inline QModelIndex firstColumn( const QModelIndex& index ) {
00039 return index.sibling(index.row(), 0);
00040 }
00041
00042 ExpandingWidgetModel::ExpandingWidgetModel( QWidget* parent ) :
00043 QAbstractTableModel(parent)
00044 {
00045 }
00046
00047 ExpandingWidgetModel::~ExpandingWidgetModel() {
00048 clearExpanding();
00049 }
00050
00051 static QColor doAlternate(QColor color) {
00052 QColor background = QApplication::palette().background().color();
00053 return KColorUtils::mix(color, background, 0.15);
00054 }
00055
00056 uint ExpandingWidgetModel::matchColor(const QModelIndex& index) const {
00057
00058 int matchQuality = contextMatchQuality( index.sibling(index.row(), 0) );
00059
00060 if( matchQuality > 0 )
00061 {
00062 bool alternate = index.row() & 1;
00063
00064 QColor badMatchColor(0xff00aa44);
00065 QColor goodMatchColor(0xff00ff00);
00066
00067 QColor background = treeView()->palette().light().color();
00068
00069 QColor totalColor = KColorUtils::mix(badMatchColor, goodMatchColor, ((float)matchQuality)/10.0);
00070
00071 if(alternate)
00072 totalColor = doAlternate(totalColor);
00073
00074 const float dynamicTint = 0.2;
00075 const float minimumTint = 0.2;
00076 double tintStrength = (dynamicTint*matchQuality)/10;
00077 if(tintStrength)
00078 tintStrength += minimumTint;
00079
00080 return KColorUtils::tint(background, totalColor, tintStrength ).rgb();
00081 }else{
00082 return 0;
00083 }
00084 }
00085
00086 QVariant ExpandingWidgetModel::data( const QModelIndex & index, int role ) const
00087 {
00088 switch( role ) {
00089 case Qt::BackgroundRole:
00090 {
00091 if( index.column() == 0 ) {
00092
00093 uint color = matchColor(index);
00094 if( color )
00095 return QBrush( color );
00096 }
00097
00098 if( isExpanded(index) ) {
00099 if( index.row() & 1 ) {
00100 return doAlternate(treeView()->palette().toolTipBase().color());
00101 } else {
00102 return treeView()->palette().toolTipBase();
00103 }
00104 }
00105 }
00106 }
00107 return QVariant();
00108 }
00109
00110 void ExpandingWidgetModel::clearMatchQualities() {
00111 m_contextMatchQualities.clear();
00112 }
00113
00114 QModelIndex ExpandingWidgetModel::partiallyExpandedRow() const {
00115 if( m_partiallyExpanded.isEmpty() )
00116 return QModelIndex();
00117 else
00118 return m_partiallyExpanded.constBegin().key();
00119 }
00120
00121 void ExpandingWidgetModel::clearExpanding() {
00122
00123 clearMatchQualities();
00124 QMap<QPersistentModelIndex,ExpandingWidgetModel::ExpandingType> oldExpandState = m_expandState;
00125 foreach( QPointer<QWidget> widget, m_expandingWidgets )
00126 delete widget;
00127 m_expandingWidgets.clear();
00128 m_expandState.clear();
00129
00130 for( QMap<QPersistentModelIndex, ExpandingWidgetModel::ExpandingType>::const_iterator it = oldExpandState.constBegin(); it != oldExpandState.constEnd(); ++it )
00131 if(it.value() == Expanded)
00132 emit dataChanged(it.key(), it.key());
00133 }
00134
00135 ExpandingWidgetModel::ExpansionType ExpandingWidgetModel::isPartiallyExpanded(const QModelIndex& index) const {
00136 if( m_partiallyExpanded.contains(firstColumn(index)) )
00137 return m_partiallyExpanded[firstColumn(index)];
00138 else
00139 return NotExpanded;
00140 }
00141
00142 void ExpandingWidgetModel::partiallyUnExpand(const QModelIndex& idx_)
00143 {
00144 QModelIndex index( firstColumn(idx_) );
00145 m_partiallyExpanded.remove(index);
00146 m_partiallyExpanded.remove(idx_);
00147 }
00148
00149 int ExpandingWidgetModel::partiallyExpandWidgetHeight() const {
00150 return 60;
00151 }
00152
00153 void ExpandingWidgetModel::rowSelected(const QModelIndex& idx_)
00154 {
00155 QModelIndex idx( firstColumn(idx_) );
00156 if( !m_partiallyExpanded.contains( idx ) )
00157 {
00158 QModelIndex oldIndex = partiallyExpandedRow();
00159
00160 if( !m_partiallyExpanded.isEmpty() )
00161 {
00162 while( !m_partiallyExpanded.isEmpty() )
00163 m_partiallyExpanded.erase(m_partiallyExpanded.begin());
00164
00165 }
00166
00167 if( !idx.isValid() ) {
00168
00169 if( oldIndex.isValid() )
00170 emit dataChanged(oldIndex, oldIndex);
00171 } else {
00172 QVariant variant = data(idx, CodeCompletionModel::ItemSelected);
00173
00174 if( !isExpanded(idx) && variant.type() == QVariant::String) {
00175
00176
00177
00178 if( oldIndex.isValid() && (oldIndex < idx || (!(oldIndex < idx) && oldIndex.parent() < idx.parent()) ) )
00179 m_partiallyExpanded.insert(idx, ExpandUpwards);
00180 else
00181 m_partiallyExpanded.insert(idx, ExpandDownwards);
00182
00183
00184 if( oldIndex.isValid() && oldIndex < idx ) {
00185 emit dataChanged(oldIndex, idx);
00186
00187 if( treeView()->verticalScrollMode() == QAbstractItemView::ScrollPerItem )
00188 {
00189
00190
00191 QRect selectedRect = treeView()->visualRect(idx);
00192 QRect frameRect = treeView()->frameRect();
00193
00194 if( selectedRect.bottom() > frameRect.bottom() ) {
00195 int diff = selectedRect.bottom() - frameRect.bottom();
00196
00197 QModelIndex newTopIndex = idx;
00198
00199 QModelIndex nextTopIndex = idx;
00200 QRect nextRect = treeView()->visualRect(nextTopIndex);
00201 while( nextTopIndex.isValid() && nextRect.isValid() && nextRect.top() >= diff ) {
00202 newTopIndex = nextTopIndex;
00203 nextTopIndex = treeView()->indexAbove(nextTopIndex);
00204 if( nextTopIndex.isValid() )
00205 nextRect = treeView()->visualRect(nextTopIndex);
00206 }
00207 treeView()->scrollTo( newTopIndex, QAbstractItemView::PositionAtTop );
00208 }
00209 }
00210
00211
00212
00213
00214
00215
00216
00217
00218 } else if( oldIndex.isValid() && idx < oldIndex ) {
00219 emit dataChanged(idx, oldIndex);
00220
00221
00222
00223
00224
00225
00226
00227 } else
00228 emit dataChanged(idx, idx);
00229 } else if( oldIndex.isValid() ) {
00230
00231
00232 emit dataChanged(oldIndex, oldIndex);
00233 }
00234 }
00235 }else{
00236 kDebug( 13035 ) << "ExpandingWidgetModel::rowSelected: Row is already partially expanded";
00237 }
00238 }
00239
00240 QString ExpandingWidgetModel::partialExpandText(const QModelIndex& idx) const {
00241 if( !idx.isValid() )
00242 return QString();
00243
00244 return data(firstColumn(idx), CodeCompletionModel::ItemSelected).toString();
00245 }
00246
00247 QRect ExpandingWidgetModel::partialExpandRect(const QModelIndex& idx_) const
00248 {
00249 QModelIndex idx(firstColumn(idx_));
00250
00251 if( !idx.isValid() )
00252 return QRect();
00253
00254 ExpansionType expansion = ExpandDownwards;
00255
00256 if( m_partiallyExpanded.find(idx) != m_partiallyExpanded.constEnd() )
00257 expansion = m_partiallyExpanded[idx];
00258
00259
00260 QModelIndex rightMostIndex = idx;
00261 QModelIndex tempIndex = idx;
00262 while( (tempIndex = rightMostIndex.sibling(rightMostIndex.row(), rightMostIndex.column()+1)).isValid() )
00263 rightMostIndex = tempIndex;
00264
00265 QRect rect = treeView()->visualRect(idx);
00266 QRect rightMostRect = treeView()->visualRect(rightMostIndex);
00267
00268 rect.setLeft( rect.left() + 20 );
00269 rect.setRight( rightMostRect.right() - 5 );
00270
00271
00272 int top = rect.top() + 5;
00273 int bottom = rightMostRect.bottom() - 5 ;
00274
00275 if( expansion == ExpandDownwards )
00276 top += basicRowHeight(idx);
00277 else
00278 bottom -= basicRowHeight(idx);
00279
00280 rect.setTop( top );
00281 rect.setBottom( bottom );
00282
00283 return rect;
00284 }
00285
00286 bool ExpandingWidgetModel::isExpandable(const QModelIndex& idx_) const
00287 {
00288 QModelIndex idx(firstColumn(idx_));
00289
00290 if( !m_expandState.contains(idx) )
00291 {
00292 m_expandState.insert(idx, NotExpandable);
00293 QVariant v = data(idx, CodeCompletionModel::IsExpandable);
00294 if( v.canConvert<bool>() && v.value<bool>() )
00295 m_expandState[idx] = Expandable;
00296 }
00297
00298 return m_expandState[idx] != NotExpandable;
00299 }
00300
00301 bool ExpandingWidgetModel::isExpanded(const QModelIndex& idx_) const
00302 {
00303 QModelIndex idx(firstColumn(idx_));
00304 return m_expandState.contains(idx) && m_expandState[idx] == Expanded;
00305 }
00306
00307 void ExpandingWidgetModel::setExpanded(QModelIndex idx_, bool expanded)
00308 {
00309 QModelIndex idx(firstColumn(idx_));
00310
00311
00312 if( !idx.isValid() )
00313 return;
00314
00315 if( isExpandable(idx) ) {
00316 if( !expanded && m_expandingWidgets.contains(idx) && m_expandingWidgets[idx] ) {
00317 m_expandingWidgets[idx]->hide();
00318 }
00319
00320 m_expandState[idx] = expanded ? Expanded : Expandable;
00321
00322 if( expanded )
00323 partiallyUnExpand(idx);
00324
00325 if( expanded && !m_expandingWidgets.contains(idx) )
00326 {
00327 QVariant v = data(idx, CodeCompletionModel::ExpandingWidget);
00328
00329 if( v.canConvert<QWidget*>() ) {
00330 m_expandingWidgets[idx] = v.value<QWidget*>();
00331 } else if( v.canConvert<QString>() ) {
00332
00333 KTextEdit* edit = new KTextEdit( v.value<QString>() );
00334 edit->setReadOnly(true);
00335 edit->resize(200, 50);
00336 m_expandingWidgets[idx] = edit;
00337 } else {
00338 m_expandingWidgets[idx] = 0;
00339 }
00340 }
00341
00342
00343 if( !expanded && firstColumn(treeView()->currentIndex()) == idx && !isPartiallyExpanded(idx) )
00344 rowSelected(idx);
00345
00346 emit dataChanged(idx, idx);
00347
00348 if(treeView())
00349 treeView()->scrollTo(idx);
00350 }
00351 }
00352
00353 int ExpandingWidgetModel::basicRowHeight( const QModelIndex& idx_ ) const
00354 {
00355 QModelIndex idx(firstColumn(idx_));
00356
00357 ExpandingDelegate* delegate = dynamic_cast<ExpandingDelegate*>( treeView()->itemDelegate(idx) );
00358 if( !delegate || !idx.isValid() ) {
00359 kDebug( 13035 ) << "ExpandingWidgetModel::basicRowHeight: Could not get delegate";
00360 return 15;
00361 }
00362 return delegate->basicSizeHint( idx ).height();
00363 }
00364
00365
00366 void ExpandingWidgetModel::placeExpandingWidget(const QModelIndex& idx_)
00367 {
00368 QModelIndex idx(firstColumn(idx_));
00369
00370 QWidget* w = 0;
00371 if( m_expandingWidgets.contains(idx) )
00372 w = m_expandingWidgets[idx];
00373
00374 if( w && isExpanded(idx) ) {
00375 if( !idx.isValid() )
00376 return;
00377
00378 QRect rect = treeView()->visualRect(idx);
00379
00380 if( !rect.isValid() || rect.bottom() < 0 || rect.top() >= treeView()->height() ) {
00381
00382 w->hide();
00383 return;
00384 }
00385
00386 QModelIndex rightMostIndex = idx;
00387 QModelIndex tempIndex = idx;
00388 while( (tempIndex = rightMostIndex.sibling(rightMostIndex.row(), rightMostIndex.column()+1)).isValid() )
00389 rightMostIndex = tempIndex;
00390
00391 QRect rightMostRect = treeView()->visualRect(rightMostIndex);
00392
00393
00394 rect.setLeft( rect.left() + 20 );
00395 rect.setRight( rightMostRect.right() - 5 );
00396
00397
00398 rect.setTop( rect.top() + basicRowHeight(idx) + 5 );
00399 rect.setHeight( w->height() );
00400
00401 if( w->parent() != treeView()->viewport() || w->geometry() != rect || !w->isVisible() ) {
00402 w->setParent( treeView()->viewport() );
00403
00404 w->setGeometry(rect);
00405 w->show();
00406 }
00407 }
00408 }
00409
00410 void ExpandingWidgetModel::placeExpandingWidgets() {
00411 for( QMap<QPersistentModelIndex, QPointer<QWidget> >::const_iterator it = m_expandingWidgets.constBegin(); it != m_expandingWidgets.constEnd(); ++it ) {
00412 placeExpandingWidget(it.key());
00413 }
00414 }
00415
00416 int ExpandingWidgetModel::expandingWidgetsHeight() const
00417 {
00418 int sum = 0;
00419 for( QMap<QPersistentModelIndex, QPointer<QWidget> >::const_iterator it = m_expandingWidgets.constBegin(); it != m_expandingWidgets.constEnd(); ++it ) {
00420 if( isExpanded(it.key() ) && (*it) )
00421 sum += (*it)->height();
00422 }
00423 return sum;
00424 }
00425
00426
00427 QWidget* ExpandingWidgetModel::expandingWidget(const QModelIndex& idx_) const
00428 {
00429 QModelIndex idx(firstColumn(idx_));
00430
00431 if( m_expandingWidgets.contains(idx) )
00432 return m_expandingWidgets[idx];
00433 else
00434 return 0;
00435 }
00436
00437 void ExpandingWidgetModel::cacheIcons() const {
00438 if( m_expandedIcon.isNull() )
00439 m_expandedIcon = KIconLoader::global()->loadIcon("arrow-down", KIconLoader::Small, 10);
00440
00441 if( m_collapsedIcon.isNull() )
00442 m_collapsedIcon = KIconLoader::global()->loadIcon("arrow-right", KIconLoader::Small, 10);
00443 }
00444
00445 QList<QVariant> mergeCustomHighlighting( int leftSize, const QList<QVariant>& left, int rightSize, const QList<QVariant>& right )
00446 {
00447 QList<QVariant> ret = left;
00448 if( left.isEmpty() ) {
00449 ret << QVariant(0);
00450 ret << QVariant(leftSize);
00451 ret << QTextFormat(QTextFormat::CharFormat);
00452 }
00453
00454 if( right.isEmpty() ) {
00455 ret << QVariant(leftSize);
00456 ret << QVariant(rightSize);
00457 ret << QTextFormat(QTextFormat::CharFormat);
00458 } else {
00459 QList<QVariant>::const_iterator it = right.constBegin();
00460 while( it != right.constEnd() ) {
00461 {
00462 QList<QVariant>::const_iterator testIt = it;
00463 for(int a = 0; a < 2; a++) {
00464 ++testIt;
00465 if(testIt == right.constEnd()) {
00466 kWarning() << "Length of input is not multiple of 3";
00467 break;
00468 }
00469 }
00470 }
00471
00472 ret << QVariant( (*it).toInt() + leftSize );
00473 ++it;
00474 ret << QVariant( (*it).toInt() );
00475 ++it;
00476 ret << *it;
00477 if(!(*it).value<QTextFormat>().isValid())
00478 kDebug( 13035 ) << "Text-format is invalid";
00479 ++it;
00480 }
00481 }
00482 return ret;
00483 }
00484
00485
00486 QList<QVariant> mergeCustomHighlighting( QStringList strings, QList<QVariantList> highlights, int grapBetweenStrings )
00487 {
00488 if(strings.isEmpty()) {
00489 kWarning() << "List of strings is empty";
00490 return QList<QVariant>();
00491 }
00492
00493 if(highlights.isEmpty()) {
00494 kWarning() << "List of highlightings is empty";
00495 return QList<QVariant>();
00496 }
00497
00498 if(strings.count() != highlights.count()) {
00499 kWarning() << "Length of string-list is " << strings.count() << " while count of highlightings is " << highlights.count() << ", should be same";
00500 return QList<QVariant>();
00501 }
00502
00503
00504 QString totalString = strings[0];
00505 QVariantList totalHighlighting = highlights[0];
00506
00507 strings.pop_front();
00508 highlights.pop_front();
00509
00510 while( !strings.isEmpty() ) {
00511 totalHighlighting = mergeCustomHighlighting( totalString.length(), totalHighlighting, strings[0].length(), highlights[0] );
00512 totalString += strings[0];
00513
00514 for(int a = 0; a < grapBetweenStrings; a++)
00515 totalString += ' ';
00516
00517 strings.pop_front();
00518 highlights.pop_front();
00519
00520 }
00521
00522 return totalHighlighting;
00523 }
00524 #include "expandingwidgetmodel.moc"
00525