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