mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-24 19:02:51 +00:00
404 lines
12 KiB
C++
404 lines
12 KiB
C++
![]() |
/* This file is part of the KDE libraries and the Kate part.
|
||
|
*
|
||
|
* Copyright (C) 2006 Hamish Rodda <rodda@kde.org>
|
||
|
* Copyright (C) 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public License
|
||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "katecompletiontree.h"
|
||
|
|
||
|
#include <QtGui/QHeaderView>
|
||
|
#include <QtGui/QScrollBar>
|
||
|
#include <QVector>
|
||
|
#include <QTimer>
|
||
|
#include <QApplication>
|
||
|
#include <QDesktopWidget>
|
||
|
|
||
|
#include "kateview.h"
|
||
|
#include "katerenderer.h"
|
||
|
#include "kateconfig.h"
|
||
|
|
||
|
#include "katecompletionwidget.h"
|
||
|
#include "katecompletiondelegate.h"
|
||
|
#include "katecompletionmodel.h"
|
||
|
|
||
|
KateCompletionTree::KateCompletionTree(KateCompletionWidget* parent)
|
||
|
: ExpandingTree(parent)
|
||
|
{
|
||
|
m_scrollingEnabled = true;
|
||
|
header()->hide();
|
||
|
setRootIsDecorated(false);
|
||
|
setIndentation(0);
|
||
|
setFrameStyle(QFrame::NoFrame);
|
||
|
setAllColumnsShowFocus(true);
|
||
|
setAlternatingRowColors(true);
|
||
|
//We need ScrollPerItem, because ScrollPerPixel is too slow with a very large competion-list(see KDevelop).
|
||
|
setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||
|
|
||
|
m_resizeTimer = new QTimer(this);
|
||
|
m_resizeTimer->setSingleShot(true);
|
||
|
|
||
|
connect(m_resizeTimer, SIGNAL(timeout()), this, SLOT(resizeColumnsSlot()));
|
||
|
|
||
|
// Provide custom highlighting to completion entries
|
||
|
setItemDelegate(new KateCompletionDelegate(widget()->model(), widget()));
|
||
|
|
||
|
///@todo uncomment this once we're sure it isn't called too often, or maybe use a timer.
|
||
|
//connect(widget()->model(), SIGNAL(contentGeometryChanged()), this, SLOT(resizeColumnsSlot()));
|
||
|
|
||
|
// Prevent user from expanding / collapsing with the mouse
|
||
|
setItemsExpandable(false);
|
||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::currentChanged ( const QModelIndex & current, const QModelIndex & previous ) {
|
||
|
widget()->model()->rowSelected(current);
|
||
|
ExpandingTree::currentChanged(current, previous);
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::setScrollingEnabled(bool enabled) {
|
||
|
m_scrollingEnabled = enabled;
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::scrollContentsBy( int dx, int dy )
|
||
|
{
|
||
|
if(m_scrollingEnabled)
|
||
|
QTreeView::scrollContentsBy(dx, dy);
|
||
|
|
||
|
if (isVisible())
|
||
|
m_resizeTimer->start(300);
|
||
|
}
|
||
|
|
||
|
int KateCompletionTree::columnTextViewportPosition ( int column ) const {
|
||
|
int ret = columnViewportPosition(column);
|
||
|
QModelIndex i = model()->index(0, column, QModelIndex());
|
||
|
QModelIndex base = model()->index(0, 0, QModelIndex());
|
||
|
|
||
|
//If it's just a group header, use the first child
|
||
|
if(base.isValid() && model()->rowCount(base))
|
||
|
i = base.child(0, column);
|
||
|
|
||
|
if(i.isValid()) {
|
||
|
QIcon icon = i.data(Qt::DecorationRole).value<QIcon>();
|
||
|
if(!icon.isNull())
|
||
|
ret += icon.actualSize(sizeHintForIndex(i)).width();
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
KateCompletionWidget * KateCompletionTree::widget( ) const
|
||
|
{
|
||
|
return static_cast<KateCompletionWidget*>(const_cast<QObject*>(parent()));
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::resizeColumnsSlot()
|
||
|
{
|
||
|
if(model())
|
||
|
resizeColumns();
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::resizeColumns(bool firstShow, bool forceResize)
|
||
|
{
|
||
|
static bool preventRecursion = false;
|
||
|
if (preventRecursion)
|
||
|
return;
|
||
|
|
||
|
if(firstShow)
|
||
|
forceResize = true;
|
||
|
|
||
|
preventRecursion = true;
|
||
|
|
||
|
widget()->setUpdatesEnabled(false);
|
||
|
|
||
|
int modelIndexOfName = kateModel()->translateColumn(KTextEditor::CodeCompletionModel::Name);
|
||
|
int oldIndentWidth = columnViewportPosition(modelIndexOfName);
|
||
|
|
||
|
///Step 1: Compute the needed column-sizes for the visible content
|
||
|
|
||
|
int numColumns = model()->columnCount();
|
||
|
|
||
|
QVector<int> columnSize(numColumns, 5);
|
||
|
|
||
|
int currentYPos = 0;
|
||
|
|
||
|
QModelIndex current = indexAt(QPoint(1,1));
|
||
|
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.
|
||
|
currentYPos += sizeHintForIndex(current).height();
|
||
|
current = current.child(0,0);
|
||
|
}
|
||
|
|
||
|
int num = 0;
|
||
|
bool changed = false;
|
||
|
|
||
|
while( current.isValid() && currentYPos < height() )
|
||
|
{
|
||
|
// kDebug() << current.row() << "out of" << model()->rowCount(current.parent()) << "in" << current.parent().data(Qt::DisplayRole);
|
||
|
currentYPos += sizeHintForIndex(current).height();
|
||
|
// itemDelegate()->sizeHint(QStyleOptionViewItem(), current).isValid() && itemDelegate()->sizeHint(QStyleOptionViewItem(), current).intersects(visibleViewportRect)
|
||
|
changed = true;
|
||
|
num++;
|
||
|
for( int a = 0; a < numColumns; a++ )
|
||
|
{
|
||
|
QSize s = sizeHintForIndex (current.sibling(current.row(), a));
|
||
|
// kDebug() << "size-hint for" << current.row() << a << ":" << s << current.sibling(current.row(), a).data(Qt::DisplayRole);
|
||
|
if( s.width() > columnSize[a] && s.width() < 2000 )
|
||
|
columnSize[a] = s.width();
|
||
|
else if( s.width() > 2000 )
|
||
|
kDebug( 13035 ) << "got invalid size-hint of width " << s.width();
|
||
|
}
|
||
|
|
||
|
QModelIndex oldCurrent = current;
|
||
|
current = current.sibling(current.row()+1, 0);
|
||
|
|
||
|
//Are we at the end of a group? If yes, move on into the next group
|
||
|
if( !current.isValid() && oldCurrent.parent().isValid() ) {
|
||
|
current = oldCurrent.parent().sibling( oldCurrent.parent().row()+1, 0 );
|
||
|
if( current.isValid() && current.child(0,0).isValid() ) {
|
||
|
currentYPos += sizeHintForIndex(current).height();
|
||
|
current = current.child(0,0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int totalColumnsWidth = 0, originalViewportWidth = viewport()->width();
|
||
|
|
||
|
int maxWidth = (QApplication::desktop()->screenGeometry(widget()->view()).width()*3) / 4;
|
||
|
|
||
|
///Step 2: Update column-sizes
|
||
|
//This contains several hacks to reduce the amount of resizing that happens. Generally,
|
||
|
//resizes only happen if a) More than a specific amount of space is saved by the resize, or
|
||
|
//b) the resizing is required so the list can show all of its contents.
|
||
|
int minimumResize = 0;
|
||
|
int maximumResize = 0;
|
||
|
|
||
|
if( changed ) {
|
||
|
|
||
|
for( int n = 0; n < numColumns; n++ ) {
|
||
|
totalColumnsWidth += columnSize[n];
|
||
|
|
||
|
int diff = columnSize[n] - columnWidth(n);
|
||
|
if( diff < minimumResize )
|
||
|
minimumResize = diff;
|
||
|
if( diff > maximumResize )
|
||
|
maximumResize = diff;
|
||
|
}
|
||
|
|
||
|
int noReduceTotalWidth = 0; //The total width of the widget of no columns are reduced
|
||
|
for( int n = 0; n < numColumns; n++ ) {
|
||
|
if(columnSize[n] < columnWidth(n))
|
||
|
noReduceTotalWidth += columnWidth(n);
|
||
|
else
|
||
|
noReduceTotalWidth += columnSize[n];
|
||
|
}
|
||
|
|
||
|
//Check whether we can afford to reduce none of the columns
|
||
|
//Only reduce size if we widget would else be too wide.
|
||
|
bool noReduce = noReduceTotalWidth < maxWidth && !forceResize;
|
||
|
|
||
|
if(noReduce) {
|
||
|
totalColumnsWidth = 0;
|
||
|
for( int n = 0; n < numColumns; n++ ) {
|
||
|
if(columnSize[n] < columnWidth(n))
|
||
|
columnSize[n] = columnWidth(n);
|
||
|
|
||
|
totalColumnsWidth += columnSize[n];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( minimumResize > -40 && maximumResize == 0 && !forceResize ) {
|
||
|
//No column needs to be exanded, and no column needs to be reduced by more than 40 pixels.
|
||
|
//To prevent flashing, do not resize at all.
|
||
|
totalColumnsWidth = 0;
|
||
|
for( int n = 0; n < numColumns; n++ ) {
|
||
|
columnSize[n] = columnWidth(n);
|
||
|
totalColumnsWidth += columnSize[n];
|
||
|
}
|
||
|
} else {
|
||
|
// viewport()->resize( 5000, viewport()->height() );
|
||
|
for( int n = 0; n < numColumns; n++ ) {
|
||
|
setColumnWidth(n, columnSize[n]);
|
||
|
}
|
||
|
// kDebug() << "resizing viewport to" << totalColumnsWidth;
|
||
|
viewport()->resize( totalColumnsWidth, viewport()->height() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
///Step 3: Update widget-size and -position
|
||
|
|
||
|
int scrollBarWidth = verticalScrollBar()->width();
|
||
|
|
||
|
int newIndentWidth = columnViewportPosition(modelIndexOfName);
|
||
|
|
||
|
int newWidth = qMin(maxWidth, qMax(75, totalColumnsWidth));
|
||
|
|
||
|
if(newWidth == maxWidth)
|
||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||
|
else
|
||
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||
|
|
||
|
if(maximumResize > 0 || forceResize || oldIndentWidth != newIndentWidth) {
|
||
|
|
||
|
// kDebug() << geometry() << "newWidth" << newWidth << "current width" << width() << "target width" << newWidth + scrollBarWidth;
|
||
|
|
||
|
if((newWidth + scrollBarWidth) != width() && originalViewportWidth != totalColumnsWidth)
|
||
|
{
|
||
|
widget()->resize(newWidth + scrollBarWidth + 2, widget()->height());
|
||
|
resize(newWidth + scrollBarWidth, widget()->height()- (2*widget()->frameWidth()));
|
||
|
}
|
||
|
|
||
|
// kDebug() << "created geometry:" << widget()->geometry() << geometry() << "newWidth" << newWidth << "viewport" << viewport()->width();
|
||
|
|
||
|
if( viewport()->width() > totalColumnsWidth ) //Set the size of the last column to fill the whole rest of the widget
|
||
|
setColumnWidth(numColumns-1, viewport()->width() - columnViewportPosition(numColumns-1));
|
||
|
|
||
|
/* for(int a = 0; a < numColumns; ++a)
|
||
|
kDebug() << "column" << a << columnWidth(a) << "target:" << columnSize[a];*/
|
||
|
|
||
|
if (oldIndentWidth != newIndentWidth)
|
||
|
if(widget()->updatePosition() && !forceResize) {
|
||
|
preventRecursion = false;
|
||
|
resizeColumns(true, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
widget()->setUpdatesEnabled(true);
|
||
|
|
||
|
preventRecursion = false;
|
||
|
}
|
||
|
|
||
|
QStyleOptionViewItem KateCompletionTree::viewOptions( ) const
|
||
|
{
|
||
|
QStyleOptionViewItem opt = QTreeView::viewOptions();
|
||
|
|
||
|
opt.font = widget()->view()->renderer()->config()->font();
|
||
|
|
||
|
return opt;
|
||
|
}
|
||
|
|
||
|
KateCompletionModel * KateCompletionTree::kateModel( ) const
|
||
|
{
|
||
|
return static_cast<KateCompletionModel*>(model());
|
||
|
}
|
||
|
|
||
|
bool KateCompletionTree::nextCompletion()
|
||
|
{
|
||
|
QModelIndex current;
|
||
|
QModelIndex firstCurrent = currentIndex();
|
||
|
|
||
|
do {
|
||
|
QModelIndex oldCurrent = currentIndex();
|
||
|
|
||
|
current = moveCursor(MoveDown, Qt::NoModifier);
|
||
|
|
||
|
if (current != oldCurrent && current.isValid()) {
|
||
|
setCurrentIndex(current);
|
||
|
} else {
|
||
|
if (firstCurrent.isValid())
|
||
|
setCurrentIndex(firstCurrent);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
} while (!kateModel()->indexIsItem(current));
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool KateCompletionTree::previousCompletion()
|
||
|
{
|
||
|
QModelIndex current;
|
||
|
QModelIndex firstCurrent = currentIndex();
|
||
|
|
||
|
do {
|
||
|
QModelIndex oldCurrent = currentIndex();
|
||
|
|
||
|
current = moveCursor(MoveUp, Qt::NoModifier);
|
||
|
|
||
|
if (current != oldCurrent && current.isValid()) {
|
||
|
setCurrentIndex(current);
|
||
|
|
||
|
} else {
|
||
|
if (firstCurrent.isValid())
|
||
|
setCurrentIndex(firstCurrent);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
} while (!kateModel()->indexIsItem(current));
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool KateCompletionTree::pageDown( )
|
||
|
{
|
||
|
QModelIndex old = currentIndex();
|
||
|
|
||
|
QModelIndex current = moveCursor(MovePageDown, Qt::NoModifier);
|
||
|
|
||
|
if (current.isValid()) {
|
||
|
setCurrentIndex(current);
|
||
|
if (!kateModel()->indexIsItem(current))
|
||
|
if (!nextCompletion())
|
||
|
previousCompletion();
|
||
|
}
|
||
|
|
||
|
return current != old;
|
||
|
}
|
||
|
|
||
|
bool KateCompletionTree::pageUp( )
|
||
|
{
|
||
|
QModelIndex old = currentIndex();
|
||
|
QModelIndex current = moveCursor(MovePageUp, Qt::NoModifier);
|
||
|
|
||
|
if (current.isValid()) {
|
||
|
setCurrentIndex(current);
|
||
|
if (!kateModel()->indexIsItem(current))
|
||
|
if (!previousCompletion())
|
||
|
nextCompletion();
|
||
|
}
|
||
|
return current != old;
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::top( )
|
||
|
{
|
||
|
QModelIndex current = moveCursor(MoveHome, Qt::NoModifier);
|
||
|
setCurrentIndex(current);
|
||
|
|
||
|
if (current.isValid()) {
|
||
|
setCurrentIndex(current);
|
||
|
if (!kateModel()->indexIsItem(current))
|
||
|
nextCompletion();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::scheduleUpdate()
|
||
|
{
|
||
|
m_resizeTimer->start(300);
|
||
|
}
|
||
|
|
||
|
void KateCompletionTree::bottom( )
|
||
|
{
|
||
|
QModelIndex current = moveCursor(MoveEnd, Qt::NoModifier);
|
||
|
setCurrentIndex(current);
|
||
|
|
||
|
if (current.isValid()) {
|
||
|
setCurrentIndex(current);
|
||
|
if (!kateModel()->indexIsItem(current))
|
||
|
previousCompletion();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#include "moc_katecompletiontree.cpp"
|