language/duchain
abstractnavigationcontext.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "abstractnavigationcontext.h"
00020
00021 #include <QtGui/QTextDocument>
00022 #include <klocale.h>
00023
00024 #include "abstractdeclarationnavigationcontext.h"
00025 #include "usesnavigationcontext.h"
00026 #include "../../../interfaces/icore.h"
00027 #include "../../../interfaces/idocumentcontroller.h"
00028 #include "../functiondeclaration.h"
00029 #include "../functiondefinition.h"
00030 #include "../namespacealiasdeclaration.h"
00031 #include "../classmemberdeclaration.h"
00032 #include "../classfunctiondeclaration.h"
00033 #include "../forwarddeclaration.h"
00034 #include "../duchainutils.h"
00035 #include "../types/functiontype.h"
00036 #include "../types/enumeratortype.h"
00037 #include "../types/enumerationtype.h"
00038 #include "../types/referencetype.h"
00039 #include "../types/pointertype.h"
00040 #include <interfaces/idocumentationcontroller.h>
00041
00042
00043 namespace KDevelop {
00044
00045 void AbstractNavigationContext::setTopContext(KDevelop::TopDUContextPointer context) {
00046 m_topContext = context;
00047 }
00048
00049 KDevelop::TopDUContextPointer AbstractNavigationContext::topContext() const {
00050 return m_topContext;
00051 }
00052
00053
00054 AbstractNavigationContext::AbstractNavigationContext( KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext)
00055 : m_selectedLink(0), m_shorten(false), m_linkCount(-1), m_currentPositionLine(0),
00056 m_previousContext(previousContext), m_topContext(topContext)
00057 {
00058 }
00059
00060 void AbstractNavigationContext::addExternalHtml( const QString& text )
00061 {
00062 int lastPos = 0;
00063 int pos = 0;
00064 QString fileMark = "KDEV_FILE_LINK{";
00065 while( pos < text.length() && (pos = text.indexOf( fileMark, pos)) != -1 ) {
00066 modifyHtml() += text.mid(lastPos, pos-lastPos);
00067
00068 pos += fileMark.length();
00069
00070 if( pos != text.length() ) {
00071 int fileEnd = text.indexOf('}', pos);
00072 if( fileEnd != -1 ) {
00073 QString file = text.mid( pos, fileEnd - pos );
00074 pos = fileEnd + 1;
00075 makeLink( KUrl(file).fileName(), file, NavigationAction( KUrl(file), KTextEditor::Cursor() ) );
00076 }
00077 }
00078
00079 lastPos = pos;
00080 }
00081
00082 modifyHtml() += text.mid(lastPos, text.length()-lastPos);
00083 }
00084
00085 void AbstractNavigationContext::makeLink( const QString& name, DeclarationPointer declaration, NavigationAction::Type actionType )
00086 {
00087 NavigationAction action( declaration, actionType );
00088 QString targetId = QString::number((quint64)declaration.data() * actionType);
00089 makeLink(name, targetId, action);
00090 }
00091
00092 void AbstractNavigationContext::makeLink( const QString& name, QString targetId, const NavigationAction& action)
00093 {
00094 if(m_shorten) {
00095
00096 modifyHtml() += typeHighlight(Qt::escape(name));
00097 return;
00098 }
00099
00100 m_links[ targetId ] = action;
00101 m_intLinks[ m_linkCount ] = action;
00102 m_linkLines[ m_linkCount ] = m_currentLine;
00103 if(m_currentPositionLine == m_currentLine) {
00104 m_currentPositionLine = -1;
00105 m_selectedLink = m_linkCount;
00106 }
00107
00108 QString str = Qt::escape(name);
00109 if( m_linkCount == m_selectedLink )
00110 str = "<font style=\"background-color:#f1f1f1;\" color=\"#880088\">" + str + "</font>";
00111
00112 modifyHtml() += "<a href=\"" + targetId + "\"" + ((m_linkCount == m_selectedLink && m_currentPositionLine == -1) ? QString(" name = \"currentPosition\"") : QString()) + ">" + str + "</a>";
00113
00114 if( m_selectedLink == m_linkCount )
00115 m_selectedLinkAction = action;
00116
00117 ++m_linkCount;
00118 }
00119
00120 void AbstractNavigationContext::clear() {
00121 m_linkCount = 0;
00122 m_currentLine = 0;
00123 m_currentText.clear();
00124 m_links.clear();
00125 m_intLinks.clear();
00126 m_linkLines.clear();
00127 }
00128
00129
00130 NavigationContextPointer AbstractNavigationContext::executeLink (QString link)
00131 {
00132 if(!m_links.contains(link))
00133 return NavigationContextPointer(this);
00134
00135 return execute(m_links[link]);
00136 }
00137
00138 NavigationContextPointer AbstractNavigationContext::executeKeyAction(QString key) {
00139 Q_UNUSED(key);
00140 return NavigationContextPointer(this);
00141 }
00142
00143 NavigationContextPointer AbstractNavigationContext::execute(NavigationAction& action)
00144 {
00145 if(action.targetContext)
00146 return NavigationContextPointer(action.targetContext);
00147
00148 if(action.type == NavigationAction::ExecuteKey)
00149 return executeKeyAction(action.key);
00150
00151
00152 if( !action.decl && (action.type != NavigationAction::JumpToSource || action.document.isEmpty()) ) {
00153 kDebug() << "Navigation-action has invalid declaration" << endl;
00154 return NavigationContextPointer(this);
00155 }
00156 qRegisterMetaType<KUrl>("KUrl");
00157 qRegisterMetaType<KTextEditor::Cursor>("KTextEditor::Cursor");
00158
00159 switch( action.type ) {
00160 case NavigationAction::None:
00161 kDebug() << "Tried to execute an invalid action in navigation-widget" << endl;
00162 break;
00163 case NavigationAction::NavigateDeclaration:
00164 {
00165 AbstractDeclarationNavigationContext* ctx = dynamic_cast<AbstractDeclarationNavigationContext*>(m_previousContext);
00166 if( ctx && ctx->declaration() == action.decl )
00167 return NavigationContextPointer( m_previousContext );
00168 return AbstractNavigationContext::registerChild(action.decl);
00169 } break;
00170 case NavigationAction::NavigateUses:
00171 return registerChild(new UsesNavigationContext(action.decl.data(), this));
00172 case NavigationAction::JumpToSource:
00173 {
00174 KUrl doc = action.document;
00175 KTextEditor::Cursor cursor = action.cursor;
00176 {
00177 DUChainReadLocker lock(DUChain::lock());
00178 if(action.decl) {
00179 if(doc.isEmpty()) {
00180 doc = action.decl->url().toUrl();
00181
00182
00183
00184 cursor = action.decl->range().textRange().start();
00185 }
00186
00187 action.decl->activateSpecialization();
00188 }
00189 }
00190
00191
00192 QMetaObject::invokeMethod( ICore::self()->documentController(), "openDocument", Qt::QueuedConnection, Q_ARG(KUrl, doc), Q_ARG(KTextEditor::Cursor, cursor) );
00193 break;
00194 }
00195 case NavigationAction::ShowDocumentation: {
00196 KSharedPtr<IDocumentation> doc=ICore::self()->documentationController()->documentationForDeclaration(action.decl.data());
00197 ICore::self()->documentationController()->showDocumentation(doc);
00198 }
00199 break;
00200 }
00201
00202 return NavigationContextPointer( this );
00203 }
00204
00205 void AbstractNavigationContext::setPreviousContext(KDevelop::AbstractNavigationContext* previous) {
00206 m_previousContext = previous;
00207 }
00208
00209 NavigationContextPointer AbstractNavigationContext::registerChild( AbstractNavigationContext* context ) {
00210 m_children << NavigationContextPointer(context);
00211 return m_children.last();
00212 }
00213
00214 NavigationContextPointer AbstractNavigationContext::registerChild(DeclarationPointer declaration) {
00215
00216 QWidget* navigationWidget = declaration->context()->createNavigationWidget(declaration.data());
00217 NavigationContextPointer ret;
00218 AbstractNavigationWidget* abstractNavigationWidget = dynamic_cast<AbstractNavigationWidget*>(navigationWidget);
00219 if(abstractNavigationWidget)
00220 ret = abstractNavigationWidget->context();
00221 delete navigationWidget;
00222 ret->setPreviousContext(this);
00223 m_children << ret;
00224 return ret;
00225 }
00226
00227 const int lineJump = 3;
00228
00229 void AbstractNavigationContext::down() {
00230
00231 if( m_linkCount == -1 )
00232 html();
00233
00234 int fromLine = m_currentPositionLine;
00235
00236 if(m_selectedLink >= 0 && m_selectedLink < m_linkCount) {
00237
00238 if(fromLine == -1)
00239 fromLine = m_linkLines[m_selectedLink];
00240
00241 for(int newSelectedLink = m_selectedLink+1; newSelectedLink < m_linkCount; ++newSelectedLink) {
00242 if(m_linkLines[newSelectedLink] > fromLine && m_linkLines[newSelectedLink] - fromLine <= lineJump) {
00243 m_selectedLink = newSelectedLink;
00244 m_currentPositionLine = -1;
00245 return;
00246 }
00247 }
00248 }
00249 if(fromLine == -1)
00250 fromLine = 0;
00251
00252 m_currentPositionLine = fromLine + lineJump;
00253
00254 if(m_currentPositionLine > m_currentLine)
00255 m_currentPositionLine = m_currentLine;
00256 }
00257
00258 void AbstractNavigationContext::up() {
00259
00260 if( m_linkCount == -1 )
00261 html();
00262
00263 int fromLine = m_currentPositionLine;
00264
00265 if(m_selectedLink >= 0 && m_selectedLink < m_linkCount) {
00266
00267 if(fromLine == -1)
00268 fromLine = m_linkLines[m_selectedLink];
00269
00270 for(int newSelectedLink = m_selectedLink-1; newSelectedLink >= 0; --newSelectedLink) {
00271 if(m_linkLines[newSelectedLink] < fromLine && fromLine - m_linkLines[newSelectedLink] <= lineJump) {
00272 m_selectedLink = newSelectedLink;
00273 m_currentPositionLine = -1;
00274 return;
00275 }
00276 }
00277 }
00278
00279 if(fromLine == -1)
00280 fromLine = m_currentLine;
00281
00282 m_currentPositionLine = fromLine - lineJump;
00283 if(m_currentPositionLine < 0)
00284 m_currentPositionLine = 0;
00285 }
00286
00287 void AbstractNavigationContext::nextLink()
00288 {
00289
00290 if( m_linkCount == -1 )
00291 html();
00292
00293 m_currentPositionLine = -1;
00294
00295 if( m_linkCount > 0 )
00296 m_selectedLink = (m_selectedLink+1) % m_linkCount;
00297 }
00298
00299 void AbstractNavigationContext::previousLink()
00300 {
00301
00302 if( m_linkCount == -1 )
00303 html();
00304
00305 m_currentPositionLine = -1;
00306
00307 if( m_linkCount > 0 ) {
00308 --m_selectedLink;
00309 if( m_selectedLink < 0 )
00310 m_selectedLink += m_linkCount;
00311 }
00312
00313 Q_ASSERT(m_selectedLink >= 0);
00314 }
00315
00316 void AbstractNavigationContext::setPrefixSuffix( const QString& prefix, const QString& suffix ) {
00317 m_prefix = prefix;
00318 m_suffix = suffix;
00319 }
00320
00321 NavigationContextPointer AbstractNavigationContext::back() {
00322 if(m_previousContext)
00323 return NavigationContextPointer(m_previousContext);
00324 else
00325 return NavigationContextPointer(this);
00326 }
00327
00328 NavigationContextPointer AbstractNavigationContext::accept() {
00329 if( m_selectedLink >= 0 && m_selectedLink < m_linkCount )
00330 {
00331 NavigationAction action = m_intLinks[m_selectedLink];
00332 return execute(action);
00333 }
00334 return NavigationContextPointer(this);
00335 }
00336
00337 NavigationContextPointer AbstractNavigationContext::accept(IndexedDeclaration decl) {
00338 if(decl.data()) {
00339 NavigationAction action(DeclarationPointer(decl.data()), NavigationAction::NavigateDeclaration);
00340 return execute(action);
00341 }else{
00342 return NavigationContextPointer(this);
00343 }
00344 }
00345
00346 NavigationContextPointer AbstractNavigationContext::acceptLink(const QString& link) {
00347 if( !m_links.contains(link) ) {
00348 kDebug() << "Executed unregistered link " << link << endl;
00349 return NavigationContextPointer(this);
00350 }
00351
00352 return execute(m_links[link]);
00353 }
00354
00355
00356 NavigationAction AbstractNavigationContext::currentAction() const {
00357 return m_selectedLinkAction;
00358 }
00359
00360
00361 QString AbstractNavigationContext::declarationKind(DeclarationPointer decl)
00362 {
00363 const AbstractFunctionDeclaration* function = dynamic_cast<const AbstractFunctionDeclaration*>(decl.data());
00364
00365 QString kind;
00366
00367 if( decl->isTypeAlias() )
00368 kind = i18n("Typedef");
00369 else if( decl->kind() == Declaration::Type ) {
00370 if( decl->type<StructureType>() )
00371 kind = i18n("Class");
00372 }
00373
00374 if( decl->kind() == Declaration::Instance )
00375 kind = i18n("Variable");
00376
00377 if( NamespaceAliasDeclaration* alias = dynamic_cast<NamespaceAliasDeclaration*>(decl.data()) ) {
00378 if( alias->identifier().isEmpty() )
00379 kind = i18n("Namespace import");
00380 else
00381 kind = i18n("Namespace alias");
00382 }
00383
00384 if(function)
00385 kind = i18n("Function");
00386
00387 if( decl->isForwardDeclaration() )
00388 kind = i18n("Forward Declaration");
00389
00390 return kind;
00391 }
00392
00393 QString AbstractNavigationContext::html(bool shorten) {
00394 m_shorten = shorten;
00395 return QString();
00396 }
00397
00398 bool AbstractNavigationContext::alreadyComputed() const {
00399 return !m_currentText.isEmpty();
00400 }
00401
00402 bool AbstractNavigationContext::isWidgetMaximized() const
00403 {
00404 return true;
00405 }
00406
00407 QWidget* AbstractNavigationContext::widget() const {
00408 return 0;
00409 }
00410
00412 static QStringList splitAndKeep(QString str, QRegExp regExp) {
00413 QStringList ret;
00414 int place = regExp.indexIn(str);
00415 while(place != -1) {
00416 ret << str.left(place + regExp.matchedLength());
00417 str = str.mid(place + regExp.matchedLength());
00418 place = regExp.indexIn(str);
00419 }
00420 ret << str;
00421 return ret;
00422 }
00423
00424 void AbstractNavigationContext::addHtml(QString html) {
00425 QRegExp newLineRegExp("<br>|<br */>");
00426 foreach(QString line, splitAndKeep(html, newLineRegExp)) {
00427 m_currentText += line;
00428 if(line.indexOf(newLineRegExp) != -1) {
00429 ++m_currentLine;
00430 if(m_currentLine == m_currentPositionLine) {
00431 m_currentText += "<font color=\"#880088\"> <a name = \"currentPosition\" >" + Qt::escape("<->") + "</a> </font>";
00432 }
00433 }
00434 }
00435 }
00436
00437 QString AbstractNavigationContext::currentHtml() const {
00438 return m_currentText;
00439 }
00440
00441
00442 const Colorizer AbstractNavigationContext::typeHighlight("006000");
00443 const Colorizer AbstractNavigationContext::errorHighlight("990000");
00444 const Colorizer AbstractNavigationContext::labelHighlight("000035");
00445 const Colorizer AbstractNavigationContext::codeHighlight("005000");
00446 const Colorizer AbstractNavigationContext::propertyHighlight("009900");
00447 const Colorizer AbstractNavigationContext::navigationHighlight("000099");
00448 const Colorizer AbstractNavigationContext::importantHighlight("000000", true, true);
00449 const Colorizer AbstractNavigationContext::commentHighlight("000000", false, true);
00450 const Colorizer AbstractNavigationContext::nameHighlight("000000", true, false);
00451
00452 }