Kate
katecompletiontree.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 * Copyright (C) 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Library General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Library General Public License 00017 * along with this library; see the file COPYING.LIB. If not, write to 00018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 * Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "katecompletiontree.h" 00023 00024 #include <QtGui/QHeaderView> 00025 #include <QtGui/QScrollBar> 00026 #include <QVector> 00027 #include <QTimer> 00028 #include <QApplication> 00029 #include <QDesktopWidget> 00030 00031 #include "kateview.h" 00032 #include "katerenderer.h" 00033 #include "kateconfig.h" 00034 00035 #include "katecompletionwidget.h" 00036 #include "katecompletiondelegate.h" 00037 #include "katecompletionmodel.h" 00038 00039 KateCompletionTree::KateCompletionTree(KateCompletionWidget* parent) 00040 : ExpandingTree(parent) 00041 { 00042 m_scrollingEnabled = true; 00043 header()->hide(); 00044 setRootIsDecorated(false); 00045 setIndentation(0); 00046 setFrameStyle(QFrame::NoFrame); 00047 setAllColumnsShowFocus(true); 00048 setAlternatingRowColors(true); 00049 //We need ScrollPerItem, because ScrollPerPixel is too slow with a very large competion-list(see KDevelop). 00050 setVerticalScrollMode(QAbstractItemView::ScrollPerItem); 00051 00052 m_resizeTimer = new QTimer(this); 00053 m_resizeTimer->setSingleShot(true); 00054 00055 connect(m_resizeTimer, SIGNAL(timeout()), this, SLOT(resizeColumnsSlot())); 00056 00057 // Provide custom highlighting to completion entries 00058 setItemDelegate(new KateCompletionDelegate(widget()->model(), widget())); 00059 00061 //connect(widget()->model(), SIGNAL(contentGeometryChanged()), this, SLOT(resizeColumnsSlot())); 00062 00063 // Prevent user from expanding / collapsing with the mouse 00064 setItemsExpandable(false); 00065 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 00066 } 00067 00068 void KateCompletionTree::currentChanged ( const QModelIndex & current, const QModelIndex & previous ) { 00069 widget()->model()->rowSelected(current); 00070 ExpandingTree::currentChanged(current, previous); 00071 } 00072 00073 void KateCompletionTree::setScrollingEnabled(bool enabled) { 00074 m_scrollingEnabled = enabled; 00075 } 00076 00077 void KateCompletionTree::scrollContentsBy( int dx, int dy ) 00078 { 00079 if(m_scrollingEnabled) 00080 QTreeView::scrollContentsBy(dx, dy); 00081 00082 if (isVisible()) 00083 m_resizeTimer->start(300); 00084 } 00085 00086 int KateCompletionTree::columnTextViewportPosition ( int column ) const { 00087 int ret = columnViewportPosition(column); 00088 QModelIndex i = model()->index(0, column, QModelIndex()); 00089 QModelIndex base = model()->index(0, 0, QModelIndex()); 00090 00091 //If it's just a group header, use the first child 00092 if(base.isValid() && model()->rowCount(base)) 00093 i = base.child(0, column); 00094 00095 if(i.isValid()) { 00096 QIcon icon = i.data(Qt::DecorationRole).value<QIcon>(); 00097 if(!icon.isNull()) 00098 ret += icon.actualSize(sizeHintForIndex(i)).width(); 00099 } 00100 return ret; 00101 } 00102 00103 KateCompletionWidget * KateCompletionTree::widget( ) const 00104 { 00105 return static_cast<KateCompletionWidget*>(const_cast<QObject*>(parent())); 00106 } 00107 00108 void KateCompletionTree::resizeColumnsSlot() 00109 { 00110 if(model()) 00111 resizeColumns(); 00112 } 00113 00114 void KateCompletionTree::resizeColumns(bool firstShow, bool forceResize) 00115 { 00116 static bool preventRecursion = false; 00117 if (preventRecursion) 00118 return; 00119 00120 if(firstShow) 00121 forceResize = true; 00122 00123 preventRecursion = true; 00124 00125 widget()->setUpdatesEnabled(false); 00126 00127 int modelIndexOfName = kateModel()->translateColumn(KTextEditor::CodeCompletionModel::Name); 00128 int oldIndentWidth = columnViewportPosition(modelIndexOfName); 00129 00131 00132 int numColumns = model()->columnCount(); 00133 00134 QVector<int> columnSize(numColumns, 5); 00135 00136 int currentYPos = 0; 00137 00138 QModelIndex current = indexAt(QPoint(1,1)); 00139 if( current.child(0,0).isValid() ) { //If the index has children, it is a group-label. Then we should start with it's first child. 00140 currentYPos += sizeHintForIndex(current).height(); 00141 current = current.child(0,0); 00142 } 00143 00144 int num = 0; 00145 bool changed = false; 00146 00147 while( current.isValid() && currentYPos < height() ) 00148 { 00149 // kDebug() << current.row() << "out of" << model()->rowCount(current.parent()) << "in" << current.parent().data(Qt::DisplayRole); 00150 currentYPos += sizeHintForIndex(current).height(); 00151 // itemDelegate()->sizeHint(QStyleOptionViewItem(), current).isValid() && itemDelegate()->sizeHint(QStyleOptionViewItem(), current).intersects(visibleViewportRect) 00152 changed = true; 00153 num++; 00154 for( int a = 0; a < numColumns; a++ ) 00155 { 00156 QSize s = sizeHintForIndex (current.sibling(current.row(), a)); 00157 // kDebug() << "size-hint for" << current.row() << a << ":" << s << current.sibling(current.row(), a).data(Qt::DisplayRole); 00158 if( s.width() > columnSize[a] && s.width() < 2000 ) 00159 columnSize[a] = s.width(); 00160 else if( s.width() > 2000 ) 00161 kDebug( 13035 ) << "got invalid size-hint of width " << s.width(); 00162 } 00163 00164 QModelIndex oldCurrent = current; 00165 current = current.sibling(current.row()+1, 0); 00166 00167 //Are we at the end of a group? If yes, move on into the next group 00168 if( !current.isValid() && oldCurrent.parent().isValid() ) { 00169 current = oldCurrent.parent().sibling( oldCurrent.parent().row()+1, 0 ); 00170 if( current.isValid() && current.child(0,0).isValid() ) { 00171 currentYPos += sizeHintForIndex(current).height(); 00172 current = current.child(0,0); 00173 } 00174 } 00175 } 00176 00177 int totalColumnsWidth = 0, originalViewportWidth = viewport()->width(); 00178 00179 int maxWidth = (QApplication::desktop()->screenGeometry(widget()->view()).width()*3) / 4; 00180 00182 //This contains several hacks to reduce the amount of resizing that happens. Generally, 00183 //resizes only happen if a) More than a specific amount of space is saved by the resize, or 00184 //b) the resizing is required so the list can show all of its contents. 00185 int minimumResize = 0; 00186 int maximumResize = 0; 00187 00188 if( changed ) { 00189 00190 for( int n = 0; n < numColumns; n++ ) { 00191 totalColumnsWidth += columnSize[n]; 00192 00193 int diff = columnSize[n] - columnWidth(n); 00194 if( diff < minimumResize ) 00195 minimumResize = diff; 00196 if( diff > maximumResize ) 00197 maximumResize = diff; 00198 } 00199 00200 int noReduceTotalWidth = 0; //The total width of the widget of no columns are reduced 00201 for( int n = 0; n < numColumns; n++ ) { 00202 if(columnSize[n] < columnWidth(n)) 00203 noReduceTotalWidth += columnWidth(n); 00204 else 00205 noReduceTotalWidth += columnSize[n]; 00206 } 00207 00208 //Check whether we can afford to reduce none of the columns 00209 //Only reduce size if we widget would else be too wide. 00210 bool noReduce = noReduceTotalWidth < maxWidth && !forceResize; 00211 00212 if(noReduce) { 00213 totalColumnsWidth = 0; 00214 for( int n = 0; n < numColumns; n++ ) { 00215 if(columnSize[n] < columnWidth(n)) 00216 columnSize[n] = columnWidth(n); 00217 00218 totalColumnsWidth += columnSize[n]; 00219 } 00220 } 00221 00222 if( minimumResize > -40 && maximumResize == 0 && !forceResize ) { 00223 //No column needs to be exanded, and no column needs to be reduced by more than 40 pixels. 00224 //To prevent flashing, do not resize at all. 00225 totalColumnsWidth = 0; 00226 for( int n = 0; n < numColumns; n++ ) { 00227 columnSize[n] = columnWidth(n); 00228 totalColumnsWidth += columnSize[n]; 00229 } 00230 } else { 00231 // viewport()->resize( 5000, viewport()->height() ); 00232 for( int n = 0; n < numColumns; n++ ) { 00233 setColumnWidth(n, columnSize[n]); 00234 } 00235 // kDebug() << "resizing viewport to" << totalColumnsWidth; 00236 viewport()->resize( totalColumnsWidth, viewport()->height() ); 00237 } 00238 } 00239 00241 00242 int scrollBarWidth = verticalScrollBar()->width(); 00243 00244 int newIndentWidth = columnViewportPosition(modelIndexOfName); 00245 00246 int newWidth = qMin(maxWidth, qMax(75, totalColumnsWidth)); 00247 00248 if(newWidth == maxWidth) 00249 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); 00250 else 00251 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 00252 00253 if(maximumResize > 0 || forceResize || oldIndentWidth != newIndentWidth) { 00254 00255 // kDebug() << geometry() << "newWidth" << newWidth << "current width" << width() << "target width" << newWidth + scrollBarWidth; 00256 00257 if((newWidth + scrollBarWidth) != width() && originalViewportWidth != totalColumnsWidth) 00258 { 00259 widget()->resize(newWidth + scrollBarWidth + 2, widget()->height()); 00260 resize(newWidth + scrollBarWidth, widget()->height()- (2*widget()->frameWidth())); 00261 } 00262 00263 // kDebug() << "created geometry:" << widget()->geometry() << geometry() << "newWidth" << newWidth << "viewport" << viewport()->width(); 00264 00265 if( viewport()->width() > totalColumnsWidth ) //Set the size of the last column to fill the whole rest of the widget 00266 setColumnWidth(numColumns-1, viewport()->width() - columnViewportPosition(numColumns-1)); 00267 00268 /* for(int a = 0; a < numColumns; ++a) 00269 kDebug() << "column" << a << columnWidth(a) << "target:" << columnSize[a];*/ 00270 00271 if (oldIndentWidth != newIndentWidth) 00272 if(widget()->updatePosition() && !forceResize) { 00273 preventRecursion = false; 00274 resizeColumns(true, true); 00275 } 00276 } 00277 00278 widget()->setUpdatesEnabled(true); 00279 00280 preventRecursion = false; 00281 } 00282 00283 QStyleOptionViewItem KateCompletionTree::viewOptions( ) const 00284 { 00285 QStyleOptionViewItem opt = QTreeView::viewOptions(); 00286 00287 opt.font = widget()->view()->renderer()->config()->font(); 00288 00289 return opt; 00290 } 00291 00292 KateCompletionModel * KateCompletionTree::kateModel( ) const 00293 { 00294 return static_cast<KateCompletionModel*>(model()); 00295 } 00296 00297 bool KateCompletionTree::nextCompletion() 00298 { 00299 QModelIndex current; 00300 QModelIndex firstCurrent = currentIndex(); 00301 00302 do { 00303 QModelIndex oldCurrent = currentIndex(); 00304 00305 current = moveCursor(MoveDown, Qt::NoModifier); 00306 00307 if (current != oldCurrent && current.isValid()) { 00308 setCurrentIndex(current); 00309 } else { 00310 if (firstCurrent.isValid()) 00311 setCurrentIndex(firstCurrent); 00312 return false; 00313 } 00314 00315 } while (!kateModel()->indexIsItem(current)); 00316 00317 return true; 00318 } 00319 00320 bool KateCompletionTree::previousCompletion() 00321 { 00322 QModelIndex current; 00323 QModelIndex firstCurrent = currentIndex(); 00324 00325 do { 00326 QModelIndex oldCurrent = currentIndex(); 00327 00328 current = moveCursor(MoveUp, Qt::NoModifier); 00329 00330 if (current != oldCurrent && current.isValid()) { 00331 setCurrentIndex(current); 00332 00333 } else { 00334 if (firstCurrent.isValid()) 00335 setCurrentIndex(firstCurrent); 00336 return false; 00337 } 00338 00339 } while (!kateModel()->indexIsItem(current)); 00340 00341 return true; 00342 } 00343 00344 bool KateCompletionTree::pageDown( ) 00345 { 00346 QModelIndex old = currentIndex(); 00347 00348 QModelIndex current = moveCursor(MovePageDown, Qt::NoModifier); 00349 00350 if (current.isValid()) { 00351 setCurrentIndex(current); 00352 if (!kateModel()->indexIsItem(current)) 00353 if (!nextCompletion()) 00354 previousCompletion(); 00355 } 00356 00357 return current != old; 00358 } 00359 00360 bool KateCompletionTree::pageUp( ) 00361 { 00362 QModelIndex old = currentIndex(); 00363 QModelIndex current = moveCursor(MovePageUp, Qt::NoModifier); 00364 00365 if (current.isValid()) { 00366 setCurrentIndex(current); 00367 if (!kateModel()->indexIsItem(current)) 00368 if (!previousCompletion()) 00369 nextCompletion(); 00370 } 00371 return current != old; 00372 } 00373 00374 void KateCompletionTree::top( ) 00375 { 00376 QModelIndex current = moveCursor(MoveHome, Qt::NoModifier); 00377 setCurrentIndex(current); 00378 00379 if (current.isValid()) { 00380 setCurrentIndex(current); 00381 if (!kateModel()->indexIsItem(current)) 00382 nextCompletion(); 00383 } 00384 } 00385 00386 void KateCompletionTree::scheduleUpdate() 00387 { 00388 m_resizeTimer->start(300); 00389 } 00390 00391 void KateCompletionTree::bottom( ) 00392 { 00393 QModelIndex current = moveCursor(MoveEnd, Qt::NoModifier); 00394 setCurrentIndex(current); 00395 00396 if (current.isValid()) { 00397 setCurrentIndex(current); 00398 if (!kateModel()->indexIsItem(current)) 00399 previousCompletion(); 00400 } 00401 } 00402 00403 #include "katecompletiontree.moc"
KDE 4.6 API Reference