kde-extraapps/kdevplatform/language/duchain/navigation/abstractnavigationwidget.cpp

294 lines
8 KiB
C++
Raw Normal View History

/*
Copyright 2007 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 version 2 as published by the Free Software Foundation.
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 "abstractnavigationwidget.h"
#include <QtCore/QMap>
#include <QtCore/QMetaObject>
#include <QScrollBar>
#include <QBoxLayout>
#include <klocale.h>
#include <ktextbrowser.h>
#include "../declaration.h"
#include "../ducontext.h"
#include "../duchain.h"
#include "../duchainlock.h"
#include <qapplication.h>
#include <qevent.h>
namespace KDevelop {
AbstractNavigationWidget::AbstractNavigationWidget()
: m_browser(0), m_currentWidget(0)
{
setPalette( QApplication::palette() );
setFocusPolicy(Qt::NoFocus);
resize(100, 100);
}
const int maxNavigationWidgetWidth = 580;
QSize AbstractNavigationWidget::sizeHint() const
{
if(m_browser) {
updateIdealSize();
QSize ret = QSize(qMin(m_idealTextSize.width(), maxNavigationWidgetWidth), qMin(m_idealTextSize.height(), 300));
if(m_idealTextSize.height()>=300) { //make space for the scrollbar in case it's not fitting
ret.rwidth() += 17; //m_browser->verticalScrollBar()->width() returns 100, for some reason
}
if(m_currentWidget) {
ret.setHeight( ret.height() + m_currentWidget->sizeHint().height() );
if(m_currentWidget->sizeHint().width() > ret.width())
ret.setWidth(m_currentWidget->sizeHint().width());
if(ret.width() < 500) //When we embed a widget, give it some space, even if it doesn't have a large size-hint
ret.setWidth(500);
}
return ret;
} else
return QWidget::sizeHint();
}
void AbstractNavigationWidget::initBrowser(int height) {
Q_UNUSED(height);
m_browser = new KTextBrowser;
// since we can embed arbitrary HTML we have to make sure it stays readable by forcing a black-white palette
QPalette p;
p.setColor(QPalette::AlternateBase, Qt::white);
p.setColor(QPalette::Base, Qt::white);
p.setColor(QPalette::Text, Qt::black);
m_browser->setPalette( p );
m_browser->setOpenLinks(false);
m_browser->setOpenExternalLinks(false);
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(m_browser);
layout->setMargin(0);
setLayout(layout);
connect( m_browser, SIGNAL(anchorClicked(QUrl)), this, SLOT(anchorClicked(QUrl)) );
foreach(QWidget* w, findChildren<QWidget*>())
w->setContextMenuPolicy(Qt::NoContextMenu);
}
AbstractNavigationWidget::~AbstractNavigationWidget() {
if(m_currentWidget)
layout()->removeWidget(m_currentWidget);
}
void AbstractNavigationWidget::setContext(NavigationContextPointer context, int initBrows)
{
if(m_browser == 0)
initBrowser(initBrows);
if(!context) {
kDebug() << "no new context created";
return;
}
if(context == m_context && (!context || context->alreadyComputed()))
return;
if (!m_startContext)
m_startContext = m_context;
bool wasInitial = (m_context == m_startContext);
m_context = context;
update();
emit contextChanged(wasInitial, m_context == m_startContext);
emit sizeHintChanged();
}
void AbstractNavigationWidget::updateIdealSize() const {
if(m_context && !m_idealTextSize.isValid()) {
QTextDocument doc;
doc.setHtml(m_currentText);
if(doc.idealWidth() > maxNavigationWidgetWidth) {
doc.setPageSize( QSize(maxNavigationWidgetWidth, 30) );
m_idealTextSize.setWidth(maxNavigationWidgetWidth);
}else{
m_idealTextSize.setWidth(doc.idealWidth());
}
m_idealTextSize.setHeight(doc.size().height());
}
}
void AbstractNavigationWidget::update() {
setUpdatesEnabled(false);
Q_ASSERT( m_context );
QString html = m_context->html();
if(!html.isEmpty()) {
int scrollPos = m_browser->verticalScrollBar()->value();
m_browser->setHtml( html );
m_currentText = html;
m_idealTextSize = QSize();
QSize hint = sizeHint();
if(hint.height() >= m_idealTextSize.height()) {
m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}else{
m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
m_browser->verticalScrollBar()->setValue(scrollPos);
m_browser->scrollToAnchor("currentPosition");
m_browser->show();
}else{
m_browser->hide();
}
if(m_currentWidget) {
layout()->removeWidget(m_currentWidget);
m_currentWidget->setParent(0);
}
m_currentWidget = m_context->widget();
m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_browser->setMaximumHeight(10000);
if(m_currentWidget) {
if (m_currentWidget->metaObject()
->indexOfSignal(SIGNAL(navigateDeclaration(KDevelop::IndexedDeclaration))) != -1)
{
connect(m_currentWidget, SIGNAL(navigateDeclaration(KDevelop::IndexedDeclaration)),
this, SLOT(navigateDeclaration(KDevelop::IndexedDeclaration)));
}
layout()->addWidget(m_currentWidget);
if(m_context->isWidgetMaximized()) {
//Leave unused room to the widget
m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
m_browser->setMaximumHeight(m_idealTextSize.height());
}
}
setUpdatesEnabled(true);
}
NavigationContextPointer AbstractNavigationWidget::context() {
return m_context;
}
void AbstractNavigationWidget::navigateDeclaration(KDevelop::IndexedDeclaration decl) {
DUChainReadLocker lock( DUChain::lock() );
setContext(m_context->accept(decl));
}
void AbstractNavigationWidget::anchorClicked(const QUrl& url) {
DUChainReadLocker lock( DUChain::lock() );
//We may get deleted while the call to acceptLink, so make sure we don't crash in that case
QPointer<AbstractNavigationWidget> thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->acceptLink(url.toString());
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::next() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
m_context->nextLink();
update();
}
void AbstractNavigationWidget::previous() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
m_context->previousLink();
update();
}
void AbstractNavigationWidget::accept() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
QPointer<AbstractNavigationWidget> thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->accept();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::back() {
DUChainReadLocker lock( DUChain::lock() );
QPointer<AbstractNavigationWidget> thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->back();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::up() {
DUChainReadLocker lock( DUChain::lock() );
m_context->up();
update();
}
void AbstractNavigationWidget::down() {
DUChainReadLocker lock( DUChain::lock() );
m_context->down();
update();
}
void AbstractNavigationWidget::embeddedWidgetAccept() {
accept();
}
void AbstractNavigationWidget::embeddedWidgetDown() {
down();
}
void AbstractNavigationWidget::embeddedWidgetRight() {
next();
}
void AbstractNavigationWidget::embeddedWidgetLeft() {
previous();
}
void AbstractNavigationWidget::embeddedWidgetUp() {
up();
}
void AbstractNavigationWidget::wheelEvent(QWheelEvent* event )
{
QWidget::wheelEvent(event);
event->accept();
return;
}
}
#include "moc_abstractnavigationwidget.cpp"